diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cd57ed263..314600267 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -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 @@ -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 diff --git a/.github/workflows/full_test.yml b/.github/workflows/full_test.yml index 341923a71..bd2621ead 100644 --- a/.github/workflows/full_test.yml +++ b/.github/workflows/full_test.yml @@ -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' @@ -57,7 +57,7 @@ jobs: - name: Test run: just test - build-no-networks: + build-no-network: runs-on: ubuntu-latest steps: @@ -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 diff --git a/.github/workflows/notebooks_test.yml b/.github/workflows/notebooks_test.yml index ef39a8eb8..aaa4d5ba8 100644 --- a/.github/workflows/notebooks_test.yml +++ b/.github/workflows/notebooks_test.yml @@ -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/ diff --git a/adr/010-optional-domain-dependencies.md b/adr/010-optional-domain-dependencies.md index c67cab5b9..fc5e9dc89 100644 --- a/adr/010-optional-domain-dependencies.md +++ b/adr/010-optional-domain-dependencies.md @@ -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 diff --git a/adr/012-network-format-constructors.md b/adr/012-network-format-constructors.md index 324959247..ef8a2bdaa 100644 --- a/adr/012-network-format-constructors.md +++ b/adr/012-network-format-constructors.md @@ -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. diff --git a/adr/013-network-topology-in-mikeio1d.md b/adr/013-network-topology-in-mikeio1d.md new file mode 100644 index 000000000..baa19d00e --- /dev/null +++ b/adr/013-network-topology-in-mikeio1d.md @@ -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. diff --git a/adr/README.md b/adr/README.md index 59b6d3bf2..fe0639ba0 100644 --- a/adr/README.md +++ b/adr/README.md @@ -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 diff --git a/docs/images/res1d_network_mapping.png b/docs/images/res1d_network_mapping.png deleted file mode 100644 index 2930e9914..000000000 Binary files a/docs/images/res1d_network_mapping.png and /dev/null differ diff --git a/docs/user-guide/network.qmd b/docs/user-guide/network.qmd index a769d9c16..8ec1af136 100644 --- a/docs/user-guide/network.qmd +++ b/docs/user-guide/network.qmd @@ -7,17 +7,18 @@ jupyter: python3 ::: {.callout-warning collapse="true"} ## Extra dependencies required -Network support depends on libraries that are **not** installed -by default, e.g. `networkx`. You can install them alongside `modelskill` using the _networks_ extra: +Network support depends on `mikeio1d`, which reads the result files and builds +the network, and which is **not** installed by default. Install it alongside +`modelskill` with the _network_ extra: ```bash -uv pip install modelskill[networks] +uv pip install modelskill[network] ``` or ```bash -uv add modelskill[networks] +uv add modelskill[network] ``` ::: @@ -25,562 +26,135 @@ uv add modelskill[networks] ```{python} # | echo: false -import pandas as pd -import numpy as np -from typing import Any -from modelskill.network import Network, NetworkNode, NetworkReach, ReachBreakPoint - - -class ExampleNode(NetworkNode): - """Node backed by an in-memory DataFrame, e.g. model output.""" - - def __init__(self, node_id: str, data: pd.DataFrame): - self._id = node_id - self._data = data - - @property - def id(self) -> str: - return self._id - - @property - def data(self) -> pd.DataFrame: - return self._data - - @property - def boundary(self) -> dict[str, Any]: - return {} - - -class ExampleReach(NetworkReach): - """Reach connecting two nodes with a given length.""" - - def __init__( - self, - reach_id: str, - start: NetworkNode, - end: NetworkNode, - length: float, - breakpoints: list | None = None, - ): - self._id = reach_id - self._start = start - self._end = end - self._length = length - self._breakpoints = breakpoints or [] - - @property - def id(self) -> str: - return self._id - - @property - def start(self) -> NetworkNode: - return self._start - - @property - def end(self) -> NetworkNode: - return self._end - - @property - def length(self) -> float: - return self._length - - @property - def breakpoints(self) -> list: - return self._breakpoints - - -class ExampleBreakPoint(ReachBreakPoint): - def __init__(self, reach_id: str, distance: float, data: pd.DataFrame): - self._id = (reach_id, distance) - self._data = data - - @property - def id(self): - return self._id - - @property - def data(self): - return self._data - - -# Synthetic model output covering the observation period -model_time = pd.date_range("1994-08-07 16:00", periods=180, freq="1min") -t = np.linspace(0, 2 * np.pi, len(model_time)) - -df1 = pd.DataFrame({"WaterLevel": 194.0 + np.sin(t)}, index=model_time) -df2 = pd.DataFrame({"WaterLevel": 193.8 + 0.8 * np.sin(t)}, index=model_time) -df3 = pd.DataFrame({"WaterLevel": 193.6 + 0.6 * np.sin(t)}, index=model_time) -df4 = pd.DataFrame({"WaterLevel": 193.9 + 0.9 * np.sin(t)}, index=model_time) - -node_s1 = ExampleNode("sensor_1", df1) -node_s2 = ExampleNode("sensor_2", df2) -node_s3 = ExampleNode("sensor_3", df3) - -bp = ExampleBreakPoint("r1", 200.0, df4) -reach1 = ExampleReach("r1", node_s1, node_s2, length=500.0, breakpoints=[bp]) -reach2 = ExampleReach("r2", node_s2, node_s3, length=300.0) -network = Network(reaches=[reach1, reach2]) -``` - -A **Network** represents a 1D pipe or river network as a directed graph: nodes hold timeseries data (e.g. water level at a junction) and reaches carry the topology and reach length between them. Break points along a reach (e.g. cross-section chainages) are supported as observation locations too. - -The typical workflow is: - -``` -Network → NetworkModelResult → match() → Comparer -``` - -## Building a Network - -You can build a `Network` object by loading it from a supported network result file. Reading these files relies on [mikeio1d](https://github.com/DHI/mikeio1d), so install the `networks` dependency group first. - -There is one constructor per product that writes the file: - -| Constructor | Extensions | Product | -|---|---|---| -| `Network.from_mike` | `.res1d`, `.res11` | MIKE 1D, MIKE 11 | -| `Network.from_epanet` | `.res`, plus optional `.resx` and `.inp` | EPANET | - -The remaining formats mikeio1d can open cannot be turned into a `Network`, and say so when you try: - -| Extension | Why not | -|---|---| -| `.out` (SWMM) | The reach connectivity is not in the `.out` at all — it lives in the companion `.inp` input file, which modelskill does not read yet ([#689](https://github.com/DHI/modelskill/issues/689)). | -| `.resx` | Not a network on its own. It holds extra results for the network defined in the sibling `.res`, so pass it as `from_epanet(res, resx=...)` instead. | -| `.prf`, `.crf`, `.xrf` (MOUSE), `.whr` (Water Hammer) | No test fixture exists for these formats, so support cannot be verified. [Open an issue](https://github.com/DHI/modelskill/issues) if you need one. | - -### From a network result file - -The quickest way to get a `Network` is from the path to a result file: - -```{python} -# | echo: false - path_to_res1d = "../../tests/testdata/network.res1d" -path_to_res11 = "../../tests/testdata/network_cali.res11" -path_to_epanet = "../../tests/testdata/epanet.res" path_to_sensor_data_1 = "../../tests/testdata/network_sensor_1.csv" path_to_sensor_data_2 = "../../tests/testdata/network_sensor_2.csv" +path_to_sensor_data_4 = "../../tests/testdata/network_sensor_4.csv" ``` -```{python} -from modelskill.network import Network - -network = Network.from_mike(path_to_res1d) -network -``` - -or a `mikeio1d.Res1D` that has already been opened: - -```python -from mikeio1d import Res1D - -res = Res1D(path_to_res1d) -network = Network.from_mike(res) -``` - -MIKE 11 files work the same way. Note that MIKE 11 keeps its timeseries on reach gridpoints rather than on nodes, so the nodes of such a network carry no data of their own: - -```{python} -Network.from_mike(path_to_res11) -``` - -EPANET results use `from_epanet`: - -```{python} -Network.from_epanet(path_to_epanet) -``` - -#### EPANET companion files - -An EPANET run writes more than one file, and the `.res` is not the whole picture: - -| File | What it adds | -|---|---| -| `.res` | The network and its main timeseries. Required. | -| `.resx` | Extra results — tank volume and pump energy. Merged onto matching nodes. | -| `.inp` | The model input. The only one of the three carrying reach lengths. | - -Pass the companions alongside the result file to get a fuller network: - -```{python} -# | echo: false -path_to_epanet_resx = "../../tests/testdata/epanet.resx" -path_to_epanet_inp = "../../tests/testdata/epanet.inp" -``` - -```{python} -network_epanet = Network.from_epanet( - path_to_epanet, - resx=path_to_epanet_resx, - inp=path_to_epanet_inp, -) -network_epanet -``` - -`Volume` and `Volume Percentage` come from the `.resx`, and the reach lengths from the `.inp`: - -```{python} -sorted( - d["length"] - for *_, d in network_epanet.graph.edges(data=True) - if d["length"] is not None -) -``` - -::: {.callout-warning} -## EPANET reach geometry is limited - -EPANET is a link-node model, and mikeio1d reports no length and a single synthetic gridpoint for each reach. So for an EPANET network: - -* without `inp=`, every edge of `network.graph` has `length=None`. A length-weighted `networkx` call then fails rather than returning a meaningless number — shortest-path treats the edge as unreachable, and anything that sums the weights raises `TypeError`. The attribute is always present, since `networkx` defaults a missing weight to `1`. With `inp=`, only pumps and valves stay `None`, since `[PIPES]` is the one section carrying lengths -* reaches have no breakpoints, so a `ReachObservation` cannot be matched — use `NodeObservation` instead -* `find(reach=..., distance=)` never resolves; only `distance="start"` and `distance="end"` work - -For the same reason, `resx=` merges node quantities only. Its reach-level quantities — pump energy, efficiency and costs — have no breakpoint to live on, which is tracked in [#680](https://github.com/DHI/modelskill/issues/680). - -Node timeseries, `to_dataframe()`, `to_dataset()`, `find(node=...)` and `recall()` are unaffected. -::: - -A MIKE 1D network contains multiple levels that are unified into a generic network structure as depicted in the image below. The image introduces concepts like _find_, _recall_ and _boundary_ which are explained in the following sections. - -![How a Res1D file maps to a Network object. Reaches and nodes are re-indexed as integers; boundary nodes expose `find()`/`recall()` round-trip lookups.](../images/res1d_network_mapping.png) - -#### Selective loading - -Large result files can contain thousands of nodes and gridpoints. Loading all of that data into memory is slow and may cause memory issues — especially when you only need the timeseries at a handful of nodes where observations exist. - -Both constructors accept the same two optional arguments to restrict what gets loaded: - -| Argument | Type | Effect | -|---|---|---| -| `nodes` | `None` \| `str` \| `list[str]` | Control which nodes have timeseries data loaded. `None` (default) loads all nodes; `[]` skips all node data; a name or list loads only those nodes. | -| `reaches` | `None` \| `str` \| `list[str]` | Control which reaches have intermediate gridpoint data populated. `None` (default) loads everything; `[]` skips all gridpoints; a name or list of names loads only those reaches. | - -::: {.callout-note} -Selective loading only controls **which timeseries are held in memory**. The full network topology (nodes, reaches, lengths) is always constructed so that `find()`, `recall()`, and graph algorithms still work on the complete network. -::: - -The most memory-efficient setup — useful when you only care about specific junction nodes — is to pass the node IDs you need and skip all intermediate gridpoints with `reaches=[]`: - -```{python} -network_subset = Network.from_mike( - path_to_res1d, - nodes=["78", "46"], - reaches=[], -) -network_subset -``` - -If you also need gridpoint data along a particular reach, pass its name (or a list of names): - -```{python} -network_subset = Network.from_mike( - path_to_res1d, - nodes=["78", "46"], - reaches=["94l1"], -) -network_subset -``` +A **network** is a 1D pipe or river network read as a graph: nodes hold timeseries +data (water level at a junction, say), reaches carry the topology and the length +between them, and break points along a reach are locations in their own right. -When only some nodes are loaded, `to_dataframe()` and `to_dataset()` only contain columns for those nodes — the rest are graph-connected but data-free: +The workflow is the usual four steps, with the network standing in for a grid or a +mesh: -```{python} -network_subset.to_dataframe(sel="WaterLevel").head() ``` - -## Inspecting the Network - -### Available quantities - -```{python} -network.quantities +result file → NetworkModelResult → match() → Comparer ``` -### Underlying graph +## Where the network comes from -The network exposes a `networkx.Graph` so you can use any NetworkX algorithm or -plotting function directly: +Reading the file and building the graph is [mikeio1d](https://github.com/DHI/mikeio1d)'s +job, not modelskill's. `Network.open` reads MIKE 1D (`.res1d`), MIKE 11 (`.res11`) +and EPANET (`.res`) results, finds the EPANET companion files, and says so when a +format cannot be turned into a network. See its documentation for the companions, +and for `resolve`, which looks up a location by the name the model gives it. -```{python} -# | echo: false - -plot_kwargs = { - "font_size": 6, - "node_size": 130, - "node_color": "white", - "edgecolors": "black", - "with_labels": True, -} -``` +modelskill takes the file path directly, and opens it for you: ```{python} -import networkx as nx -import matplotlib.pyplot as plt +import modelskill as ms -fig, ax = plt.subplots(figsize=(10, 9), layout="tight") -nx.draw(network.graph, ax=ax, **plot_kwargs) -plt.show() +mr = ms.NetworkModelResult(path_to_res1d, name="MyModel", item="WaterLevel") +mr ``` -### Timeseries data +A model result reads no timeseries when it is built. Each observation's location is +read when it is matched, and only that location, so scoring a few sensors against a +large model reads a few series. -```{python} -# Multi-index DataFrame: columns are (node, quantity) -network.to_dataframe().head() -``` +Open the network yourself when you need to name EPANET companion files, or to build +model results for several quantities over one network: ```{python} -# Select a single quantity -network.to_dataframe(sel="WaterLevel").head() -``` - -## Looking up node IDs - -After construction, nodes are re-labelled as integers. Use `find()` to go from original coordinates to the integer ID and `recall()` to go back. - -::: {.callout-tip} -When creating `NodeObservation` objects for skill assessment you generally do **not** need to call `find()`. You can pass the original string ID as `node=`, and `NetworkModelResult` will resolve it for you during matching. For breakpoints, use `at=(reach, distance)` rather than `node=`. See [Skill assessment workflow](#skill-assessment-workflow) for details. -::: +from mikeio1d.network import Network -```{python} -# Look up a named node by its original id -node_id = network.find(node="117") -print(f"Node '117' → integer id {node_id}") - -# Recover the original label -print(network.recall(node_id)) -``` - -```{python} -# Look up a break point by reach + chainage -bp_id = network.find(reach="94l1", distance=21.285) -print(f"Break point (94l1, 21.285) → integer id {bp_id}") -print(network.recall(bp_id)) -``` - -```{python} -# Node batch lookup -ids = network.find(node=["20", "113", "38"]) -print(ids) -``` - -```{python} -# Reach lookup -ids = network.find(reach="58l1", distance="start") -print(ids) -ids = network.find(reach="58l1", distance=[51.456, 77.185]) -print(ids) -ids = network.find(reach="58l1", distance=["start", 77.185]) -print(ids) +network = Network.open(path_to_res1d) +ms.NetworkModelResult(network, name="MyModel", item="WaterLevel") ``` ## Skill assessment workflow -### 1. Wrap the Network in a NetworkModelResult +### 1. The model result -```{python} -import modelskill as ms -from modelskill.model.network import NetworkModelResult - -mr = NetworkModelResult(network, name="MyModel", item="WaterLevel") -mr -``` +`mr` above is the model side of the comparison, for one quantity. It reads a +location when an observation is matched there. ### 2. Create NodeObservations and compute skill `NodeObservation` accepts a file path directly; the observation name is taken from the filename. -The `at=` argument can be specified in three ways, depending on what information you have at hand. +The `at=` argument takes either of two forms, depending on what you have at hand. ::: {.callout-note} ## MIKE 1D vocabulary vs the modelskill API -In MIKE 1D, "nodes" are the named connection points in the 1D network (manholes, junctions, outfalls) while "reaches" are the pipe or channel segments that connect them. The modelskill API uses `NodeObservation` more broadly: the `at=` argument accepts either a node ID (int or string) **or** a `(reach_id, distance)` breakpoint tuple that identifies a specific chainage along a reach. If you only need a reach-level quantity (uniform across the reach), use `ReachObservation` with `reach=` instead. +In MIKE 1D, "nodes" are the named connection points in the 1D network (manholes, junctions, outfalls) while "reaches" are the pipe or channel segments that connect them. The modelskill API uses `NodeObservation` more broadly: the `at=` argument accepts either the node's name **or** a `(reach_id, position)` break point tuple that identifies a specific chainage along a reach. If you only need a reach-level quantity (uniform across the reach), use `ReachObservation` with `reach=` instead. ::: -#### Option A — original string alias +#### Option A — the node's name -Pass the original node identifier from the source format (e.g. the Res1D node name) as a plain string. The `NetworkModelResult` resolves it to the correct integer ID at match time, so you do not need to call `network.find()` yourself: +Pass the node's name in the model, e.g. the Res1D node name. It is resolved against the network at match time: ```{python} -obs_1 = ms.NodeObservation(path_to_sensor_data_1, at="78") -obs_2 = ms.NodeObservation(path_to_sensor_data_2, at="46") +obs_1 = ms.NodeObservation(path_to_sensor_data_1, at="98") -cc = ms.match(obs=[obs_1, obs_2], mod=mr) +cc = ms.match(obs=obs_1, mod=mr) cc.skill() ``` ::: {.callout-note} -Resolution happens inside `ms.match()`. If the string is not found in the network's alias map a `ValueError` is raised with a clear message indicating which alias could not be resolved. +Resolution happens inside `ms.match()`. A name the network does not hold raises a `ValueError`. ::: -#### Option B — breakpoint by `(reach, distance)` tuple +#### Option B — breakpoint by `(reach, position)` tuple -When your observation sits at a chainage along a reach rather than at a named junction node, you can use the `at` argument and pass a `(reach_id, distance)` tuple. The `NetworkModelResult` looks up the corresponding breakpoint at match time: +When your observation sits at a chainage along a reach rather than at a named junction node, you can use the `at` argument and pass a `(reach_id, position)` tuple. The `NetworkModelResult` looks up the corresponding breakpoint at match time: -```python -obs_bp = ms.NodeObservation(path_to_sensor_data_1, at=("94l1", 21.285)) +```{python} +obs_bp = ms.NodeObservation(path_to_sensor_data_2, at=("117l1", 48.7)) cc = ms.match(obs=obs_bp, mod=mr) cc.skill() ``` -The tuple form is equivalent to calling `network.find(reach="94l1", distance=21.285)` beforehand and is resolved during matching. +The tuple form is resolved during matching, as `network.resolve(("117l1", 48.7))` would resolve it. ::: {.callout-note} ## Chainage tolerance -Breakpoint distances are matched with a tolerance of **1 × 10⁻³** (i.e. ±0.001 in whatever distance units the network uses). This means that small floating-point discrepancies between the distance you type and the value stored in the network are handled gracefully. If no breakpoint falls within that tolerance a `ValueError` is raised. +By default a position matches a break point within the tolerance mikeio1d uses to decide two chainages are the same place, which absorbs floating-point rounding between the position you type and the value stored in the network. A measured chainage is rarely that close; pass `position_tol=` to snap it onto the nearest break point within that distance that carries the item being scored: + +```python +obs_bp = ms.NodeObservation(path_to_sensor_data_2, at=("117l1", 48.0), position_tol=1.0) +``` + +The comparer keeps the chainage you gave; `mr.extract(obs_bp).at` gives the break point the model was read at. If no break point falls within the tolerance, a `ValueError` says the location is not found and points to `network.addresses(reach=...)`, which lists the break points along that reach. One is raised too when the location exists but carries no data for the quantity you are scoring - MIKE 1D stores water level and discharge at alternating grid points along a reach. ::: ### 3. Using ReachObservation for reach-uniform quantities Some physical quantities — such as **discharge in a pipe** — are conceptually uniform across the whole reach, even though the model stores values at individual breakpoints along the reach. `ReachObservation` lets you associate a timeseries with a named reach without having to identify a specific breakpoint. -When matched against a `NetworkModelResult`, modelskill automatically extracts model data from an arbitrary breakpoint that belongs to the given reach and verifies that all breakpoints on the reach carry equivalent values. +When matched against a `NetworkModelResult`, modelskill extracts model data from the breakpoints on the given reach that carry the quantity and hold data for it. When there are several, they must carry equivalent values, and the one with the lowest position is used. The comparer sits on the reach, so `cc.at` is the reach name; `mr_q.extract(obs_q).at` gives the break point the model was read at. ```{python} -obs_q = ms.ReachObservation(path_to_sensor_data_1, reach="94l1", name="Q_94l1") +obs_q = ms.ReachObservation(path_to_sensor_data_4, reach="116l1", name="Q_116l1") obs_q ``` Pass the observation to `ms.match()` exactly as you would a `NodeObservation`. modelskill resolves which breakpoint to use automatically: ```{python} -mr_q = NetworkModelResult(network, name="MyModel", item="Discharge") +mr_q = ms.NetworkModelResult(network, name="MyModel", item="Discharge") cc_q = ms.match(obs=obs_q, mod=mr_q) cc_q.skill() ``` ::: {.callout-note} -Use `ReachObservation` when your measured quantity is representative of the whole reach (e.g. discharge, which is constant along a reach in steady flow). If you need to compare a quantity that varies spatially along the reach (e.g. water level at a specific chainage), use a `NodeObservation` with a `(reach, distance)` tuple instead (see [Option B](#option-b-breakpoint-by-reach-distance-tuple) above). +Use `ReachObservation` when your measured quantity is representative of the whole reach (e.g. discharge, which is constant along a reach in steady flow). If you need to compare a quantity that varies spatially along the reach (e.g. water level at a specific chainage), use a `NodeObservation` with a `(reach, position)` tuple instead (see [Option B](#option-b-breakpoint-by-reach-position-tuple) above). ::: -## Development - -### Custom network formats - -In case you have your network data in a format that is not included in [Building a Network](#building-a-network), you can assemble a `Network` object by subclassing the abstract base classes `NetworkNode` and `NetworkReach`. - -`NetworkNode` requires three properties: `id`, `data`, and `boundary`. -`NetworkReach` requires four: `id`, `start`, `end`, and `breakpoints`. - -`NetworkReach.length` is optional and defaults to `None`. Reach length matters in some domains (rivers, sewer networks) and not in others (link-node water distribution models), so override it only where a length exists. Where it is left undefined, the reach contributes an edge with `length=None` to `network.graph`, which keeps length-weighted graph algorithms from quietly treating the reach as free. Nothing else in modelskill reads the length — matching and extraction work from break point distances alone. - - -The following is a simple implementation example: - -```python -import pandas as pd -import numpy as np -from typing import Any -from modelskill.network import NetworkNode, NetworkReach, Network - - -class ExampleNode(NetworkNode): - """Node backed by an in-memory DataFrame, e.g. model output.""" - - def __init__(self, node_id: str, data: pd.DataFrame): - self._id = node_id - self._data = data - - @property - def id(self) -> str: - return self._id - - @property - def data(self) -> pd.DataFrame: - return self._data - - @property - def boundary(self) -> dict[str, Any]: - return {} - - -class ExampleReach(NetworkReach): - """Reach connecting two nodes with a given length.""" - - def __init__( - self, reach_id: str, start: NetworkNode, end: NetworkNode, length: float, - breakpoints: list | None = None, - ): - self._id = reach_id - self._start = start - self._end = end - self._length = length - self._breakpoints = breakpoints or [] - - @property - def id(self) -> str: - return self._id - - @property - def start(self) -> NetworkNode: - return self._start - - @property - def end(self) -> NetworkNode: - return self._end - - @property - def length(self) -> float: - return self._length - - @property - def breakpoints(self) -> list: - return self._breakpoints -``` - -::: {.callout-tip} -The three abstract properties that **every** `NetworkNode` subclass must implement are `id`, `data` and `boundary`. If `boundary` is not relevant for your use case, define the property to return an empty dictionary, as in the example above. Similarly, a `NetworkReach` with no intermediate points can return an empty `breakpoints` list, and one with no meaningful length can leave the `length` property out altogether. -::: - - -```{python} -from modelskill.network import Network - -# df1, df2 and df3 are DataFrame objects that are loaded in memory -node_s1 = ExampleNode("sensor_1", df1) -node_s2 = ExampleNode("sensor_2", df2) -node_s3 = ExampleNode("sensor_3", df3) - -reach1 = ExampleReach("r1", node_s1, node_s2, length=500.0) -reach2 = ExampleReach("r2", node_s2, node_s3, length=300.0) - -network = Network(reaches=[reach1, reach2]) -network -``` - -### Adding break points along a reach - -Break points represent intermediate chainage locations on a reach (e.g. cross-sections). Subclass `ReachBreakPoint` the same way — implement `id` (a `(reach_id, distance)` tuple) and `data`: - -```python -from modelskill.network import ReachBreakPoint - - -class ExampleBreakPoint(ReachBreakPoint): - def __init__(self, reach_id: str, distance: float, data: pd.DataFrame): - self._id = (reach_id, distance) - self._data = data - - @property - def id(self): - return self._id - - @property - def data(self): - return self._data - - -# df4 is a DataFrame object that has been loaded in memory -bp = ExampleBreakPoint("r1", 200.0, df4) -reach1 = ExampleReach("r1", node_s1, node_s2, length=500.0, breakpoints=[bp]) -reach2 = ExampleReach("r2", node_s2, node_s3, length=300.0) -network = Network(reaches=[reach1, reach2]) -``` - - ## See also * [API reference — NetworkModelResult](../api/NetworkModelResult.qmd) diff --git a/justfile b/justfile index d027887a5..e33c86c5c 100644 --- a/justfile +++ b/justfile @@ -23,9 +23,9 @@ test: typecheck: uv run mypy src/ --config-file pyproject.toml -# Run doctests in metrics.py +# Run doctests in metrics.py and types.py doctest: - uv run pytest src/modelskill/metrics.py --doctest-modules + uv run pytest src/modelskill/metrics.py src/modelskill/types.py --doctest-modules # Generate HTML coverage report coverage: diff --git a/notebooks/Collection_systems_network.ipynb b/notebooks/Collection_systems_network.ipynb index 89e9962e0..f2d78685c 100644 --- a/notebooks/Collection_systems_network.ipynb +++ b/notebooks/Collection_systems_network.ipynb @@ -1,1629 +1,1817 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "fdb0d0b9", - "metadata": {}, - "outputs": [], - "source": [ - "import modelskill as ms\n", - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "import networkx as nx\n", - "import matplotlib.pyplot as plt\n", - "\n", - "from modelskill.network import Network" - ] - }, - { - "cell_type": "markdown", - "id": "b643e568", - "metadata": {}, - "source": [ - "# 1D network workflow\n", - "\n", - "This notebook shows how to use `modelskill` to evaluate model results from 1D network simulations, such as collection systems or river networks. The workflow follows the same four-step pattern used elsewhere in `modelskill`: define model results → define observations → match → compare.\n", - "\n", - "## Loading network results\n", - "\n", - "The `Network` class organises data from a network simulation (e.g. a sewer system or a river) into a form that `modelskill` can work with. It stores time-series data for every node and break point in the network, and exposes the topology via a `networkx` graph.\n", - "\n", - "### Loading from a supported format\n", - "\n", - "The easiest way to create a `Network` is to load it directly from a supported file format. Currently **Res1D** (MIKE 1D) is supported:\n", - "\n", - "```python\n", - "from modelskill.network import Network\n", - "\n", - "network = Network.from_mike(\"path/to/results.res1d\")\n", - "``` \n", - "\n", - "### Custom network format\n", - "\n", - "For other simulation tools you can build a `Network` from your own data by subclassing the abstract base classes `NetworkNode` and `NetworkEdge`. Notice that this approach requires that you build the logic to generate a list of `NetworkEdge` to pass it to the network.\n", - "\n", - "```python\n", - "from modelskill.network import Network, NetworkNode, NetworkEdge\n", - "\n", - "class MyNode(NetworkNode): ...\n", - "\n", - "class MyEdge(NetworkEdge): ...\n", - "\n", - "\n", - "def generate_list_of_edges(a_network: CustomNetwork) -> list[MyEdge]: ...\n", - "\n", - "\n", - "edges = generate_list_of_edges(custom_network)\n", - "\n", - "network = Network(edges)\n", - "``` \n", - "\n", - "#### Break points\n", - "\n", - "Edges can optionally contain **break points** — intermediate locations along a reach (e.g. cross-section chainages) that carry their own time-series data. You can include them with subclass `EdgeBreakPoint`.\n", - "\n", - "### Example" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "cd363bae", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\n", - "Reaches: 118\n", - "Nodes: 259\n", - "Quantities: ['WaterLevel', 'Discharge']\n", - "Time: 1994-08-07 16:35:00 - 1994-08-07 18:35:00" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "network = Network.from_mike(\"../tests/testdata/network.res1d\")\n", - "network" - ] - }, - { - "cell_type": "markdown", - "id": "33a451d3", - "metadata": {}, - "source": [ - "All time-series data stored in the network can be accessed as an `xarray.Dataset` with `to_dataset()`. The dataset has one variable per physical quantity and uses the network's integer node IDs as the `node` coordinate:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "2a2d7414", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset> Size: 231kB\n",
-              "Dimensions:     (time: 110, node: 259)\n",
-              "Coordinates:\n",
-              "  * time        (time) datetime64[ns] 880B 1994-08-07T16:35:00 ... 1994-08-07...\n",
-              "  * node        (node) int64 2kB 0 1 2 3 4 5 6 7 ... 252 253 254 255 256 257 258\n",
-              "Data variables:\n",
-              "    WaterLevel  (time, node) float32 114kB 195.4 194.7 nan ... 188.5 nan nan\n",
-              "    Discharge   (time, node) float32 114kB nan nan 5.72e-06 ... nan 0.01692 0.0
" - ], - "text/plain": [ - " Size: 231kB\n", - "Dimensions: (time: 110, node: 259)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 880B 1994-08-07T16:35:00 ... 1994-08-07...\n", - " * node (node) int64 2kB 0 1 2 3 4 5 6 7 ... 252 253 254 255 256 257 258\n", - "Data variables:\n", - " WaterLevel (time, node) float32 114kB 195.4 194.7 nan ... 188.5 nan nan\n", - " Discharge (time, node) float32 114kB nan nan 5.72e-06 ... nan 0.01692 0.0" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "network.to_dataset()" - ] - }, - { - "cell_type": "markdown", - "id": "a972271a", - "metadata": {}, - "source": [ - "`Network` also exposes the underlying `networkx.Graph` via the `graph` property. This graph contains the full network topology — each graph node stores the node's data and boundary metadata — making it straightforward to run graph-based analyses (shortest path, connectivity checks, etc.):" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "06e8c2cb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "network.graph" - ] - }, - { - "cell_type": "markdown", - "id": "885523e1", - "metadata": {}, - "source": [ - "Querying the underlying `networkx` graph lets you run topology-based analyses (e.g. shortest path, connected components) directly on the network. The visualisation below plots the graph, highlighting boundary/junction nodes with a thicker outline:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "1d068e44", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_network(g: nx.Graph):\n", - "\n", - " g = g.copy()\n", - " lengths = nx.get_edge_attributes(g, \"length\")\n", - " max_len = max(lengths.values()) if lengths else 1.0\n", - " nx.set_edge_attributes(\n", - " g,\n", - " {e: v / max_len for e, v in lengths.items()},\n", - " \"norm_length\",\n", - " )\n", - "\n", - " widthmap = [2 if 'boundary' in g.nodes[node] else 1 for node in g.nodes()]\n", - " plot_kwargs = {\n", - " \"font_size\": 6,\n", - " \"node_size\": 130,\n", - " \"node_color\": \"white\",\n", - " \"edgecolors\": \"black\",\n", - " \"linewidths\": widthmap,\n", - " \"with_labels\": True,\n", - " }\n", - " fig, ax = plt.subplots(1, 1, sharey=True, layout=\"tight\", figsize=(10, 9))\n", - "\n", - " n = g.number_of_nodes()\n", - " k = 10 / np.sqrt(n) # increase multiplier (5, 10, ...) until nodes stop overlapping\n", - "\n", - " pos = nx.kamada_kawai_layout(g, weight=\"norm_length\", scale=10)\n", - " nx.draw(g, ax=ax, pos=pos, **plot_kwargs)\n", - "\n", - " # Set limits explicitly AFTER draw, otherwise matplotlib auto-scales them away\n", - " xs, ys = zip(*pos.values())\n", - " pad = 0.5\n", - " ax.set_xlim(min(xs) - pad, max(xs) + pad)\n", - " ax.set_ylim(min(ys) - pad, max(ys) + pad)\n", - " plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "53ab2b9c", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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RYTw2cLVjc65JUc2ep24lIgvHEYf4u0fCEsiA5KDQQO2h2kRnOSf+8EC1j9lyhRbNssjhSS58HrEkBef4QeiGGEfvX3+O4XoA5xScoNxMvmfv31UQiLC1T/YB1pw+YFC3FciFwjtDwtmBg3JTU9peTjU5D97uF4Q7uQph48zy0cohs+Zd8xZf7TI94MD06ZNO6adWhz26NByBlYYuGAgiOsg3LaSEHiPj3hJqR2YEOkFiW4hhDiGCDtmCO5EwhERGV5wU9SIAmsS2uJYh9BXCj2RfkGubKJV+xG1iG/cSt9KjWJptErCUfXtuWJBoSnfZzu5B1EoRK0kAs41bq13eNkeioD5ZSFuEO+JtJvCcaWXMt8TyW0fkTaIK4QVRbl89W7/wFUnn5pIBvLOCaUOt4oKPxDmiGsEezjkmfDp6667zsKYY4U8M+iCW83gR/QyCXOmLVly6QoM1HDOE/ku5JxyjXBOyA1PLu/6WAIRjXPvCfd4Z3CEKJKw6CaKgygHivv5QROEe0Z0+sWxx3H844QQQmR6+LovVaqU+/777+3vr7/+2p166qnu0ksvtdfh1Vdfdc2bN7fne/fudX///bdbsmSJu+OOO9yYMWPs9REjRriWLVum4Z4Ikbk+l8cdd1zC0/OZnDZtmnvhhRfcwoUL3T///GOvn3jiia527drunnvucVdddZXLkiXLYW/HokWL3MqVK93u3btdrly5XJkyZdwll1xy2PvE/K+99pobPXq0++GHH+xvvmuKFStm3x2NGjVyJ598cmT6gwcPum3btrldu3a5nDlzurx587rjjz8+yTJ/+eUXN3bsWLd69Wr3xx9/uBw5crjChQu72267zRUqVMj9F3bu3Om2b99uxyxPnjy2reLoU6tWLbsO3nrrLTv3119/vRs+fLjLmjWrGzBggHvppZfst+eBBx5wF154oVu8eLHr0qWLO+2009xPP/3kPv/8c1tO48aN3euvv35YnyMhUhuJbiGEOEbgBr1atWr2vGrVqm7BggVu3bp1dkMzefLkQ6bnhnn9+vWuV69e7osvvnAVKlSw18uXL29/CyHSFsTq77//bgIVoXi4QluItARh3bNnT/sdatGihbv//vttwGjKlClu0KBB7pRTTnHffPONO//8893cuXNdtmzZbD4GaJhu+fLlkUGnN99809188806oSLdom9nIYQ4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EOXKlcu98sorrn379lalP7olnhBCCCGEEOmZNBHdiEgP4iya/v37uwceeCAiVrwgo43XlClTrL91eLowr732mgknxByttoD2WtHTIYoAkQ1M79cTb3viEW57RTssBHw0VatWdVdffbWrU6eO/U3P7e7du9tymb5cuXIxlx09H8KfdmW0Dvv000/dqaeeekgbslhi/e2337Z2atWrVzdx7GEZDFI0a9bMBhJSWo7nnXfesZZfCCeEK8scPXq0tQfzQnnWrFkpinj45JNPkvQepyVbeIBm2rRpbuDAgdZvPBFoEUe7sy+++CIyqBGGwQAGC7yAvuiii9z48ePdTz/9ZP3R2Q9anHG82E7fTo5jxf4cOHAg5noR+Exz22232SAR13aePHnsGqtXr54dZ3/dHevwmURojxo1Kmav+2j4fDIPg0MDBgxIlW0UQgghhBAiw4rucE9s3MAwa9eutf9xqr1o3rFjh/3PTTduKW4oQs1P50GkI5qaNGlif//1118R8RieDrww3rZtm/2PGPLribc98cAx9iDkYokynF3EcteuXe1vBPfIkSNdnz59bN2I2FhEz4fg6N27tzmEuLrsB+56SmIdhxynG5ea5x6ca3qL814sopfjwZ1kGxC4MHTo0IjLDS1btkzIufe92tknD/vroSfz6aef7mrXru0qVKhg105yzjnLQUT369fPRC8ufjSdO3eOnGt6oXMt0YvdD8B42Df6muOcsw3w3nvvuXHjxh2yTKIhcOErVapkQr9Hjx42uMAx4ZytW7fO+spzzhYvXuyOZfjMM8jBoM2VV15p1xjHnwEbok8YGOLBsQaOJ+f4iiuucPnz53fTp0+3AREhhBBCCCEyAmkiugmP9rz11ltJ3lu2bJmFniJQ5s6da47h1KlT3b59++x9XDFEJg6on46b87vvvttCkHlwM897vI5YQjiHp0OMI6h4nbBV71LiRCJA421PeP4w4X3Yv3+/hV57WK53fxGMfsCB13H6CNGGcJhzcvMVLlzYlo+Yw/3jOITD3uOJ9dy5c7saNWrYcy8gAQcZ4rm30csBjhEh4LjzfhufeOIJ9+WXX0amCTvXyYl4lk/kAqLfD7LgOH/22We2b2PHjnV33HGHbTMDE4k452wLgwsPP/ywhdWHzynnHAceOPZETnCNJccll1ziJkyYEPk7Wsh///33JrZ/++03Ozdr1qyxdTMowfYi8pcuXWrikVBpBgrY7mMVBsb4nLRr187Nnz/frgEGtfgccXw4dzy4TuCpp56y6+Djjz+2zx6RJfwthBBCCCFERiBNRPett94aEVgjRowwQTtnzhzXunVrc4pxFgnv5aa7S5cuJobvvPNOC9XFIcOlxB0OT4cjSg44wg/3l/d3795t70VPh2jv27evibmww4oDi7hCEMbbHj8/0xGSzXv33ntvkvBwXF4/P6HSzIdzR4gxDix069bNtWnTxj3++OO2vatWrUqy3njzEfqMO8hr5B3jsBKqnpJYJ38Z4YfY8SHX5CzjMCJ4GVgIE285gLD0opn5GAghdzm8HawnERE/ePBgy9VlfT6dANGNa1ysWDGLRMAV53pJ1DnHnUbYMpDAPmzdujXyHufPQ34w4fm40CzbO6xMT4g4r+Hge0e8TJky9pywdR6A0MaFZ1occ57HSi9ggIRzgJhk3zh/nPPkYNsnT55suemI+ksvvdQ+Kxyz6AiRjMT69eut3gKDLT41g3PJceMzy7njO4JrKlz/wA8McV3I6RZCCCGEEBmGtKrgRrsrXx26Z8+ecaejYjRVtlu0aHFY/bNpMUYbom3btsWd5vHHH49sQ/Xq1YM6depYlWwqeicKFciZn97U4bZhU6dOTWh++hMzPW2RktvWWFx//fVWvZyq6/ny5bNq7fT99tXFaS22cOFCm5Z9atCggVUo99tImyx44IEHgtNOOy2oVatWissBWoUtXrzYzkepUqWsyjlQ4dsvm1ZQ/jWqhDMtleXht99+s2UC1cPpjc5x9/OyzmXLlkWO7bhx4yLrjq4qTzV2qppTQd4vGzZv3hw5H+wX1ecfe+yxSG/zE044warF++rwVKEPQ9uqUaNGBUOGDIm8Rg9wv40PP/ywvdanTx9rkbZhw4aEz9vvv/8enHvuuXZNx4LK9P369bNzwbo4H1Tvpr0a1yjts2ijR1X/w71m0gP33XefXYfhdnzsI9eH358xY8YE7du3t+efffaZVaMvXLiw9aPnnNPnXQghhBBCiIxAmolubqR96yoeCOB4otqLHQROIsJ76NChNn3fvn1jvk9bsB49eiRpC4XAW7p0qbXMQtjs3bs3xfV8/vnnQc6cOU00IpQQaH55CP5JkybFnZdWWgMGDEjSoxrhz7YlAn3Cwy2tvMhNjm+//dbEtZ/vgw8+sNdpfcbfI0eODA4H5me+uXPn2t8zZsyILJve4TwSEfFw7bXXRuYdMWKEvca5pjf6008/HVd0A320L7nkkkO2z/f15rgi7hHnfh3FihWLTIfoPvvss227EHX+GosW3UuWLInMj+ClFV2BAgVMDPvl5M2bN7KfiPpYbdcAUc1Aix8k8NBzvF69enY90KOawYdoNm3aZJ+FPHny2H4k2pM8vUCveI4Tx5kBCET0ihUrDhls43WgNR7XKJ8Zjg3i2wtyIYQQQggh0jtpJrrhmWeeSSJ8S5YsaSKHvszeGZ05c6Y5un4aHPKvv/465vK4cUcA+Wm5sac3NeKYntz8z99nnHFGkvXiNntwTHERL7300mDWrFkx+0wjlBDMTFepUqVg165d9jrb3LRp0yTLRmwh3uhDjKDGQR00aJCJpfB0uJ4IdcTaxo0b4x4zenfj2DKPF5U8cD/ZJr8tYTiO9CM//fTTI9MzsBAewKBXMu9Hi594ICgRP6VLl44sh4GHQoUK2fIZUPniiy8SWhaCCgHqe4eH+2HTl7xMmTKRdaTknCPigGWceuqpJrbD+8mgCuvBWfdwXogEYLqWLVta/+hYopv1+uOHEEQ88hzXP55jHqvXuu/zjtvOteDhWmvcuLEdi3feeSeh48Y5KF68+CHiPT0zf/78yGANAzN+0IZj5QedGAS788477fnll18e2T9EN/NOmDAhDfdACCGEEEKIDCK6ETlPPPFEEvHpHwiSWK8Tysv/iCzc8eeee85cQwQNryO0Ys0X64EwDIseD+Ic55RpihQpYqHErAfHFdGHKMKBxe2MdsQRguEw60QeiDeOxaJFi8y9REATwo04QZwxCIFL3b1790h4dO/evU3khsP0eeTIkSNo27atRQfgXOPc+jBl/yhbtuwh4px1MOiB48v+pyT2CA/GOY52WX24PA+WtXz58mSXRWjx//73v8g8nTt3TvI+4pPXP/3004Sc87vuussGQhgMYL7hw4cnWR7ONK8ToRArqoD1+XSHaNH91ltvJbl2+J/IgZQcc2Cg6N57702yLgZkwiHmpCiwzDfeeCNIlNWrVwe5c+cOOnXqlOK0OOSvvPKKDc4MHDgwePXVV4MdO3YEqQ3HheuH/Wegx59HhDTXJg53zZo1IyH7DIQxLeKba5TPgB/MEEIIIYQQIr2TpqLbg5tNCG5ywpQcWAQ6jiSipEaNGuZkI34Rqtyok/uLkEKg4RgiXmMti9d5/5NPPklWGHz88cfmXCOkcKER9Nz0sx2I4eTmJSfVC/d4D/aBfQ+DCCJ0HPcyenqEYocOHUyAexDeiLlEBT5h3N4NjoZ9Qtww3dVXX2156V7csB4GAcgLJ/SZwQiiB6IhPBoXOLzNDBYQAh6GnGsGTcJRB7jP0SKQ9ZKbTYh4okILx5hBC1zz6Pz8Jk2aRNbnc8XDAxBEQnDuYonuG264ITIvbvjdd99tTnNKjnl03rkHYcnx9JB7TvSCJ1a4uh+o4Lr3YfZdu3aNua8eBiTCnwfOCYMzfhCLbSV0PjVhsIvtSTQaAlauXGnb7fPphRBCCCGEyAikC9HtQUQgKhEYCFYcS4p94QCSP3u44Ozh6iGOEM/8z9+8nhogvhD2rBexi/giXJoib999912K8+I4464yyEAI7h9//BF3evLRyQH24dPhByKZ3GKWkVJOPMJx7Nix5iZHRxd4YUzOfKwwdg/FsHAsw9tA3jnLJKwdJzg6kqFo0aKHCPNw7jjTI1AR9cmBSKdoGk50rJx68qr9Orm+OB4MfLC9bFfz5s3tWkN8s68MfuAi//DDDxF3G7ecacirxnWNRdgxj5d3Xq5cOXPuO3bsaMti2V7wxwtXB/bvyiuvjIhuv23Rrj6DD+wHyyWd4dlnn00yqMHngIEP9of5ieZIDUi1IDKF65JBl3jpImHWrl1rgy8MeqWFOy+EEEIIIUSmEN3iv7Nz504bpECAEYI9ceLE4Oeff/5Xy/rqq68sHBmxRnGzDz/8MOEK8gwQUHE7XrRBOEybAQGEWHLguhPWT4726NGjDxHfhPUzOIHLjsAnvD4WbP+FF14YWT8F9VLaJyIDwoMICGSYMmWK/e2LnUU75mxnrLxzYH8ZSLjoootMEHMc2O5wukKscHUE9j333HNIQTn2Gxc/uko420daRHL7yACCn5ZzfTRhIKlgwYLm4HO+GIjAeSdFJFb0CGkPuOKIcwZmcPmFEEIIIYTISEh0i6MKReEo/IbQCottwq0Ji0ZEHk7Fe0KwmZ+UApxvQqMJnc6fP7+9Tt7ve++9l+xypk+fnqRyPnngFLiLl2Lg88N5sB++qBeCGqeaSAaIdswZeIhXsZ3cd1IW/GADxyicHx4vXJ1oBsR4tOimEB5V9D0IWrY3HB6fErSO47gkl3bxb2Efhg0bZmHxhND7fG0GKqiNwICKj2bgumDQ4tZbb7UoC17neUoDM0IIIYQQQqRHjuOftO4VLo4N/vrrL/fHH3+4HDlyuJNPPvlfL2f16tXu5ZdfdkuXLnW7du1yp556qitRooS766673MUXX5zQMgYPHuw6deoU+fv44493N954o7v66qtd9uzZ3a+//urGjRvnli1bFpnm9NNPdwsXLnSlSpWKvNa7d2/39NNPu1WrVrn8+fMntO7du3e7Cy+80FWrVs2NGTPGXnvmmWfcI488Yu/FYubMme711193p5xyinvppZfcHXfc4R544AFbDlx//fX2/4wZM+z/yy+/3J100klu/vz5LlH++ecfO47ly5d348ePjznNn3/+6d588033zTff2LHnPBYsWNA1atQo7v4zT9u2bW1f27VrZ/vKtoXZvn27Gz16tHvjjTfs2PO1lDdvXjsnLVu2dGeffXbC+yGEEEIIIUR6QqJbHLOMGjXK3X333W7//v0pTlukSBH3zjvvuOLFiyd5fcuWLa5s2bLuzDPPdHPnzjVhntLAw8033+wWLVrkPvvsM1eyZEl7fdKkSa5x48Zu7dq1rnDhwvYaAxQMKMBDDz1k0w4fPtyEN6L3/PPPt3WeeOKJrlChQm7Tpk2ucuXK7n//+5/tG+KYda1bt85deumlkcEChC3TnXPOOfb3ww8/bIMNMGjQINe1a1f3008/ubPOOiuy3evXr3dDhgxxr7zyituxY8ch+5U1a1Z30003uY4dO7oqVapEXv/hhx9c/fr13cqVK92wYcNc8+bNUzzWQgghhBBCZCrS2moXIi356aefLK+bcPdYOeeEQlPcLLkCbuR00/qKNlhUeI+XP/3ll19a+Duh1HPmzEnyHn3Faf3VpUuXyGuxCrx5wuHlTMe2UpCNwmsU0yP83k8fqyAbRdxiQQ414dwUywtvR6wCffEeVBfnGBDGzz7REs7nvQshhBBCCHGsIadbCOfM7SYU+5dffnF79+51uXLlcqVLl7ZHIhBe3rBhQ/f111+bI92qVStznAlbxzXGef78888tFHvChAmuUqVKhyzjvvvuc2PHjrXpDyf8ntDyjRs3usWLF7vjjjvO3XbbbeYw46YDTjfrY3uuuOIK17dvX9tGnGzc7qFDhyZx6NlGlvH4449bWHvdunXdwYMH7T1cdRz5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uG7durlPPvnELV261BUqVMjEOKK7SpUqNh3F1p544gn33XffuTZt2lhYOuK7bt26kdBzIYQQQgghMjMS3UKIf8Xdd9/t3njjDffpp59GwshTAgFOJfPFixe7smXL6sgLIYQQQohMj8LLhRD/in79+lnl8lq1armVK1emOP2QIUNMcPfs2VOCWwghhBBCHDNIdAsh/hWnn366mzNnjuVnV6hQwT344IMWOh7m4MGDbsaMGe6aa65xHTt2tAfh50IIIYQQQhwrKLxciKNEEATuo48+cqNGjTIxunv3bnfqqae6YsWKuRYtWphQPe644zL88d+xY4fr2rWre/XVV91ff/3lypUrZ/ne+/btc19//bUVW6O9GAKcdmGIc3LBwxXNhRBCCCGEyKxIdAtxFMT22LFj3TPPPGOiMx7kND/wwAOuSZMmGVp8L1u2zJxs3woMENS5cuUyFxzB/dNPPyWZ59Zbb3VjxoxxWbNmTYMtFkIIIYQQIvWQ1STEEeTvv/92rVq1cnfcccchgjtaWH/11VcmPgm5xgXOiKxfv95de+21EcF97rnnuqefftpt2bLFbd++3Xp5Mw3F1m677baIuz1+/HjXvn17G6AQQgghhBAiMyOnW4gjBAKyZcuWFk7uoWXWPffc4+rVq+dOOeUU61M9ZcoU98ILL7gvvvgiMh3TUGgsvTrev//+u/v555/d3r17LUT8vPPOcyeccIK7/vrr3TvvvGPTXHbZZfY8T548cZfz9ttvuwYNGtjgBLz77rtWiE0IIYQQQojMikS3EEeIV155xUQ3nHjiiRZi3rhx45jTbtu2zb388svukUceibjcr7/+uoWap6dBhEWLFtkAwZtvvhkRynD22WebeB46dKj9TQ43YebJCW4P/blbt25tz2+44QYT4kIIIYQQQmRWJLqFOEIC9cILL3Tfffed/T1x4kTXqFGjJNMcOHDAKnk///zzbu7cuUneI++5QIECbvny5S5HjhxH7ZwQ9v3hhx+a6Ie8efO6atWq2f9htm7daqJ64cKF1oO7bdu25mSTo03YOG49Odk43+w7205BNcLIOQ6Eks+aNcv16tXLwsjh/ffft5Zh7OuqVatsHTj7P/zwgznnQgghhBBCZEZUxUiIIwDi1AvuKlWqmOAmJPvqq6+213G977//frdu3TqrYH7++edbETEqfhO2PXr0aAvN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tlEIIYQQQogjjUS3EOKos3XrVlegQAHXs2dP16NHj4TnGzlypDniK1ascMWLFz+q2yiEEEIIIcTRQC3DhBBHnVdeecVagjVt2tRVqFDB5ciRwy1fvtz98ccfrkaNGq5q1ar2P5XM4Y477nCXXnqpGzNmjOV0v/TSSzpLQgghhBAiQyLRLYQ46gwfPtw1btzY3O533nnHNWjQwF4/4YQT3Lhx49zChQtd165d3YABAyLzjBo1yl6n8BrPyfUWQgghhBAioyHRLYQ4qhw8eNCtXbvW3GxE9hlnnBF5j0Jq+fPnt+cnnniiy5Ll/76ScMVbt27trr76anfOOee433//3ULUhRBCCCGEyGhkTesNEEJkbvbu3Wv/J9f6a//+/a53795uxIgR9vfTTz9tLcXI5b7pppvsNVqKCSGEEEIIkdGQ0y2EOKqccsop5lzv3Lkz7jR33XWXa9eunStatKj9jeCGEiVKuH/++cee08tbCCGEEEKIjIZEtxDi6H7JZMniypcv76ZPnx7z/UcffdQVLlzYcr49u3btsv+3bNnitm3b5goWLJgkLF0IIYQQQoiMgsLLhRBHnbZt27qWLVu6H374wbVv394tXbrUrVy50vp19+nTx1WpUsXNnz/fVapUyT3xxBOuWbNmbvv27W7fvn1W4fyBBx6I5HsLIYQQQgiRkVCfbiFEquR1U7n85ptvjuRtJwI9vZ966in3888/uzPPPPOobqMQQgghhBBHA1lHQoijDkXUnnzySTdy5EhzsoMgSKi3d9++fd3DDz8swS2EEEIIITIsCi8XQqQKFEvbuHGj6969u1Ul79atmytZsuQh0/34449u0KBBbsiQIa5NmzbukUce0RkSQgghhBAZFoWXCyFSFcLLEd703b7yyitd7dq1Xa5cudzu3bvdvHnz3KxZs6xSOdM8+OCDVvlcCCGEEEKIjIpEtxAi1aFA2pQpU9xLL71kRdUolnb88ce7s846y6qZN2nSxFqNCSGEEEIIkdGR6BZCpDnkeF977bWW+z116tS03hwhhBBCCCGOGCqkJoRIcwghL1GihOV6CyGEEEIIkZlQITUhRLoA0f3CCy+4v//+251wwgkus/H999+7GTNmWC77wYMH3WmnneaqV6/uLr/8cuWtCyGEEEJkYiS6hRDpRnQfOHDArV271p5nBv755x/35ptv2mDCBx98EHOaCy+80LVr187dfvvtFl4vhBBCCCEyF8rpFkKkCzZv3uzy5ctnOd033nijy+hQjZ2CcO+8805C0yO+Z86c6c4999yjvm1CCCGEECL1UE63ECJdQOVyWocRhp3R+euvv6wVWlhwFytWzD3zzDPmeH/44YfulVdecZUrV468v3z5cgs1/+WXX9Joq4UQQgghxNFATrcQIt1QsWJFV7x4cTdmzBiX0di2bZsJ6TfeeMOtWrXK/fnnn1aVnVZohJffeeedMXO3Fy9ebI74mjVr7O+yZcu6zz77zGXNquwfIYQQQojMgES3ECLdgDD97rvvTHRmFHbt2uU6d+7sXnvtNfv75ptvdkWKFDGB/dNPP7nJkyebAL/pppvc0KFDzdGP5tdff3WVKlVyP/74o/09bdo0V69evVTfFyGEEEIIceSR6BZCpBv69+/v+vXr53bu3JkhKnpv2bLFXX311W79+vWue/furkWLFi5v3rxJpnn//fddy5Yt3c8//+xOPPFEc7ZPOeUUK572xx9/uKpVq7pHH33U8rmvu+46m6dWrVru3XffTaO9EkIIIYQQRxLFLwoh0gWEYtMuDOe4TZs21lLrzDPPdHXr1nVFixZ16Q3c6xtuuMFc6o8++siVKlUq5nRUYv/222+tVdgll1xibcJwtV988UV3zjnnRKa79tpr3fnnn29u95w5c9wPP/zgChcunIp7JIQQQgghjgYqpCaESPMq388995wrWbKk69mzp702fPhw99RTT7kHHnjACpBdc8017u233zZhnl4qrTds2NBca4qlxRPcQEX2k08+2RUsWNA1a9bM/f777xY+f//997saNWq4jz/+2KbLkiWLa968eWS+JUuWpMq+CCGEEEKIo4tEtxAizaAnN4XD7r33Xrdy5cq40+H8kuNMwTEqgx8O+/fvN5FMKDhO+n+BAmm33HKLtfWaPXu2tTYrV66c+/zzz829JlSc91nP888/7ypUqGAPenUTgv7FF1/Y+xs3bnR9+vRx48ePt333hJ1vQuyFEEIIIUTGR+HlQog0gfBpWmQRnu0h9Jq8aMKsDxw4YGL2pZdeihQYmzRpkonRGTNmuBNOOCHusv/55x/LiaZqOLnS/A1UBEe8k0995ZVXHlbeOK2+ENk41Z727dvb/4jw+fPnm6P90EMPubfeesvWvWzZMhP9tAbLmTOnGz16tIWlU+WcfWvatKntB/vKtoUHFLJly3aYR1QIIYQQQqRHJLqFEKnOvn373PXXXx8R3IRnT5gwwV144YVJpkOEE2JOGy7E+N69e8315rXBgwfHXDZh2Y0bN3arV6+2HOpnn33WFSpUyELTcdZHjhzprrrqKlsXyyXnOiVwqClyxvoBcU1RtAIFCrjffvvNnX322REBz+uEipOPjcAmR50iahMnTrR2aJA9e3YT6Yh4joVvD4awD4elCyGEEEKIjI+qlwshUp1XX33V3XbbbfYc0UshstNPPz3ZeT788EOrFI5IxR2mHVd0+y2mqV27tuWHDxkyxF122WWHuNmI74ULF7p77rnHwrznzp1rIe7xwH3+3//+53755Rf7GwFPuDrb4aHQW9u2bU3MUwSO5SPuH3/8cctZP3jwoIWhA9M9+eSTtjwcffLYGYBAmLNsXHkEN+Hoybn5QgghhBAiY6CcbiFEqkPotefll1+OCO7XX3/dnXHGGfYcEUrlcsLAe/Xq5a644grXqVMne4+caURtdLg6oeOXXnqpOcYVK1aMGT7Oa9WqVXOLFi0yMY2DvWnTprjbihuOQMa99iIcp3369Okm2Al5L1++vOvSpYsrU6aMbS8ON9XJcdsRzxSDo3UY24ULjxtO5XMKqiG4oW/fvpEw+LvuukuCWwghhBAikyDRLYRIVb7++mv36aef2vOLLrrIValSxZ7jBiNwyY+GBx980IQrYpU+1oCL7IX0sGHDklQzp9r5SSed5KZOnWp9sFMid+7cJpwRyFRPjweO+fHHH28h5Tj0FEWbN2+erW/EiBHW6gtRniNHDsvbfuKJJ6xIGtOTl822kNfttxXn+/vvv3fnnXee/c3rCG5y1wFB3rp16399fIUQQgghRPpColsIkarQZstz++23R0Q0LjdtuHCUcbLXrVt3SFstwrGpEA4bNmywfGogb3rcuHG2vFq1apkAXr58uQlq8sJ5UEUcJxoID2e6m2++2RxxxHM4XNyzYsUK214ENCHwtPwiV5t8bELIEc4UbGvVqpVtN+Hu9Bd/+umnLd+biuYUUSOU3Tvlr732mtuzZ48VUaPgGiHzPXr0iKxz6NChSaqYCyGEEEKIjI0KqQkhUpVwKywvLnG5CdOeNm2aGzhwoNu2bZtbunSpFR/zodgUMwvPA99++63lbSNkCfsmXxqhjksOiGVfnIzK4YR6Q//+/S0cHDFOaDjrmzJlirXzCkNvcEK+WX7p0qUPKXDmi6bRX5sHoeo47TjXrJeBA18kze8nrjrCnMrtiP8wbJdcbiGEEEKIzIWcbiFEqhJuhYUTDbjUjRo1irjBhH6Tb12wYEGrDO7bakG4rRaiGWFNr2vypilE5nPCoyF0nXUA7bpw0BHE5IrjUCPgo6HXNm42BdGi3XMccgYIHnvssYioJ+wc8U3ONyHnhK97CCPv2LGjhZbj0IcFNw4+gw4MBAghhBBCiMyFRLcQIlXJnz9/5PmCBQvs/++++86NHTvW8qMpPta1a1eXJ08ec8UJxfZttciNJswb+BtRy3wXX3yxucfxYDmIXKqaA+HrXuDnypXLBgL++OOPJPPQwowK6fTifuedd1yDBg3sdUQ+Djgu98MPP+xuuukme53t9KKZVmWIcvLOYceOHe6OO+6wAnI+t/vUU0+1Imr0EV+zZo2F1gshhBBCiMyHwsuFEKkKOcw42QhhhC1uMWHVHsK9CcEmj5uwcoS2L6RG6Lbv7U2Pa1/5m8JsVCOPB7nTVDb34JwTNo7w/v3338299iLcQ1svBDLbG3bPcdybNGliFdWfeeYZy9+GWbNm2XNENNSsWdNyxVu2bGnh6SwLR595WbfagQkhhBBCHBvI6RZCpCrZs2d3d955pz3HwaYKeJgvv/zS/qcAGX23fVstwsrD07Zr1y7ynCrohIfTZiyl0HLwbcUQ0Ij1LVu2uGeffdZdcMEFtlwGApYtW2bTEi5OaDlutQ85p1UYRdj4n2rsDBDg2tPqy0/HPFu3bjUnnlB58snJGcdRD1czF0IIIYQQmRs53UKIVIeCZ4MHDzbHFyFNyDl52fHAicYh/uqrr+zvUqVKWV6159Zbb7XiacOHD7eCaxRhW7lypbUYI/yb0PISJUpEpid8/bbbbrOq4bjYhLLjWhO6Tl42rco8gwYNMuHsxTSu+o8//miCGyFN320GBZYsWWK52bj4DBBQUd1XYmfbCS9/7733Ir24CWunGjrHgv05kiDoOQ4MBLDdtFIjP75+/frW1kwIIYQQQqQexwWyW4QQaQBuMgXHPHXq1LH86WuuuSYS6o0rTEg24ea07wL6XtOuq2zZskmWh3gl/JxCZcnld4fxYhx3u1+/fpHXqSyOe812UImcdSNee/fubdNSsA3RTQ9v3Gucc19dHQE/e/ZsCyv/5ptvLAecQQNaltEmjOrnCHGKsr3yyisWLk8uOz3A8+bN+5+OKQ46x4vccVqdkTfO9jAIgJtPlAHOPQXdwoMQQgghhBDi6CHRLYQ4KhC6TS9tcrcpVEarL9xdD8IT4ffDDz8kmY/pELXMT4G13bt3R94jZBthTb50NLjP5INfeOGFVviMaZMDEU2+9qZNm0ygUiXdw3J8P3Gql7OtCFrcYsLOcbUR0BRTQ2zjILNNVCDHXS5atKhbtWqV9SCnSjo56+XKlTtkG1gmrcpw+Tk2hLyHC815GBslVJ1tJhccdx5BHYbjTMg7y6hdu7YNDiDmjz/+eHuf0HsiAXhs377dxLkvDsffVF8fM2aMHUeOOY4454fBAyIJGOwQQgghhBD/ApxuIYQ4UmzatCno06dPUKBAAZKWI4+sWbMGjRs3DhYsWBD8888/Qfv27YPjjz8+6NKlS3DuuecmmTbWo3LlysHSpUuTXfeiRYuCHDlyBGXLlg0+/PBDW080vDZ37tygZMmSwemnnx4sWbLkkGmqVKmSZN1XXHFF8M033wR79+4NLrzwwsh0lSpVCv7+++9g7dq1Qe3atYM6deoE119/fVCuXLkgS5YsQcOGDYP9+/eneMyY/5xzzgnKlCkT/Pnnn5HXt23bFgwYMCAoUqTIIcejVq1awbRp02z9bBfHJ3fu3HZ8k4PlN2nSxLbv9ddfD9q0aRNky5Yt2WOfK1euoHv37gntixBCCCGESIpEtxDiiICY7d+/v4nrk08+OWjVqlUwe/bs4MsvvzQBPGjQoKBYsWIm4ryIfOmll2xehCMCEiF5yimn2HvHHXdccMYZZwStW7cOvvrqq4S3AxFdvHhxW8ZFF11k650yZUrw5ptvmoD171188cXBypUrYy7j1ltvjQjOyy+/3AYHxowZY+9Vq1Yt2LFjR7B7924T92F4rWjRorbtpUqVCv7666+Et3vx4sW2vtGjRwcHDx4MHn744eCkk05KcTCiYMGCQf369e2Yf/rppwmti+UzD+cqenmcIwYNzj///EPeu+aaa4I9e/YkvE9CCCGEECIIFF4uhDgi0KN6wIAB9v9DDz1kBcViRNa4OXPmWAGxgwcPWvh4OKw7HHZNWLQPjT5cKFZGQTRym+nlzbqA0GxCsAm9vuKKKyz8Oxa0/yLHHMiJJrfcF0Sj8vpjjz1m29izZ08rokZoNsXXCKcnT9xXOed19pHia0WKFLGwb9i7d68tjzB12qGxPv86ofgUZCNUPQzb60PdqehOGLiH40RxOPLPE4VQdfLLqSBPKH6rVq3c3Xff7YoXL56kkjz7Rb4564W6detaSPy/PTdCCCGEEMcaEt1CiP8M/ahbt25tbbeSq0LuWbdunRUrI38ZQXo0BRy54eQ7I7AZCEhkXYh22nx5Yfvuu+9a27FRo0ZZYTQGDS677DJ7Tj40eeZUJicvm77f5EP/9NNPJmipqk7BOHLNPYhxKp/T65uc9sKFC5uIL1asmL3uYVs7dOhggwTkiYe3j8ELcsXJ4WbfaF3WokWLFEW+h3zvX375xa1evdry0mm7Fg9atzG4QEV2GDZsmLVHE0IIIYQQKaM+3UKI/wQuMs4vxbYSEdyAY4uIxUml6NnRJGvWrFYVHMc6UXFP1XLEroce37QYQxDff//9bsaMGeZ443zfc8895oR3797dxLAvnobwpuBZSn3DEdzehQ8XmuPvt99+21qWhQW33z5EMxXUKTxXq1Ytc6g5lr44GgMCCHIeiPYbb7wxMj+t0eDiiy+2gQEGCpIDlz3svA8dOlR9xoUQQgghEkSiWwjxn0Do4ered999SV5//fXXTXTSLoue2jxom1WmTBl7v3LlyvY3IeDpEUS3DzFHlCKkCZ2n4jlh5IhVQrTff/99Cwen3zjiG0GcXC9sXHdC0EuWLJnkdaIEGjZsaGIb6Bvu1x8PBhEQ6ghuX9U8JZEPtGBj0ADRDjjd8c4Tbc0Q3bjyCHygFdqiRYsSPJJCCCGEEMc2WdN6A4QQGRtaUNGnOtwSC/cboXfuuedGHNdwWLUHB/aOO+6w12gTlp7AIZ84caK1AyOUmzD18ePH2wNxi9hFQG/bti3JfKeffnqSNmfRvPXWW65evXpJXmP5hHAzUEGeOO25aNVFD++bbrrJBDUC+7XXXrO8bUQvIePkceOuJ+fgR4t8epyXLl3anP8bbrjBjRw50pYR7zwRSk/YOoQHCthWxLgQQgghhEgeOd1CiP8E/airVq2a5DUEGa4trm9yjqsXbWvWrEmXZ4ECY4SSP/LIIyZSPfTMZpvDgpsQ9t69e1uYPQIaJzwW0ccAAd2nTx83duxYc80BUY4IZpk4yvT+JrwdgYwDzt/z5893jz/+uE2D856oyB88eLBr3769DSr44mjRRe/C20gvcmDQgbQAD/ngQgghhBAiZSS6hRD/iT179rjs2bMncbnJ/23cuHGKYdU4un4Z6RVcZiqM//zzzyaMr7zySnPwEar8z9/jxo2z9wnBbtOmjYlyKnwTHo6LTZE53GPC1DkGJUqUiCy/U6dOVoyNQmVNmza1gQqeAw62H7ggdL1UqVIREYzTfcEFF5hbzcBAtOMeT+QzWMDfhMpPmzbNxPcll1yS7Hl68sknLa/8rLPOirxGOLoQQgghhEgZhZcLIf4T5C8j1DwIUERdtMsdK6zaz5dcDnR6gVZezZs3t0dyIFbJ/8aR/vjjjyM52h6Kx4WhtRl8//33JqppqxaeZ+nSpSbkOVYIeLjmmmvMAS9fvnxkoIPK6jjlTL9y5Uqbh9D0aJG/bNky+3/27NlWXI188JNOOinZ89StWzerwk4+uydWSzghhBBCCHEocrqFEP8JhB/F1MgLBlpW4QhTXZt2VB07dozpuAI9tHFuw+20MgOEmSN+6Xvte4Qnx8aNG80V9wMV4R7cuND05aZCPLnVFStWNPFNdAGvr1271kR6v3793JAhQ2xZn3zyieXKk3ceLfI9LIPw8nBf7ljnyYfJ47qTYx7eLiGEEEIIkTLq0y2E+E8g8KhEPmvWLBPa0YIc0UdY9VVXXZVEACLSCVlm3ldffTXTnQX2CeFL+DeVzenBHQ3HgB7guNIIYFxpQAjjfPMagxLkU1933XUmlGvWrGk52fxPLv3ixYvdli1brJDd/9femcDLWL/v/1H6klKWypL9R0h2su8tFFmzR9ZS1ooQslSWI2tlT7aUXZZIEmkhW+RLWbKTSAsR1fxf7/v3+8x/zjjHOXGOc87M9X69pjPLM8/MPPNML9fnvu7rvuGGG2xeeFSv5aCSTlgbgWxU79OmTWshdq4yHvw9sYBAwBrvhYUEbO1Y0ukjz5QpUzwdPSGEEEKI0EGiWwhxTSDiihUrZhZxrNIIsthACjg9zFiwy5QpE5LfAnO2qU6fPn3arNl8XoQqAnbnzp3ehAkTvP3791v1et68eZYU7hLEEdhUql944QXrw0aQE4LGMcZqfvHiRXusXr16tj1p4yx60FvOa3bo0CGSrfzChQv2GszYpkLuYEEA63hM3zHzyMeNG2e3qYST7C6EEEIIIWJGolsIcc0gBBF89DtPnDgxRuGNsKxRo4b1DpN0nixZspD9FhC7WLaZR/7VV1/576eCjXhlbBqim2MQGEDHTGwWJJih3aBBAwtia9my5RVfi7nhiOjx48ebMC9YsKCFn/EeqJw78Y+7gHA4B+ns9G2TmB4MLgXE/eTJk+02VnY+BwstQgghhBAiZiS6hRBxZqdu1aqVhYjRf1yqVKnLxDQJ24hyBB/WaHq6sTiHCySQk1SO4Gaed2CAGVABZ4yaq0QjvBlVRsWb+dpRze2mcs5c8Jw5c1qYGsnn9GHjHsACXrlyZRPT2bJls7FjroebMWWIbQfvhwo5dvjUqVPb+2QRgGA8LOWB3zMVeSGEEEIIETskuoUQ/xrCwbZv324iEKFIX3DhwoW9zz//3OZzIwIJ2qJCyxxpKq1UbKn4IsTbt2/vDR8+3D/+Svx/6M8uX768hdA5cAM0btzYjjvHj95tKt9Yyfv06WMhaohyer3duDHSyXETAKIbAd+kSRM79lmyZDHLON8Bo8NiA98Vr8k+hBBCCCFE7JHoFkLEGuZPv/3222ZfPnDgQKTHmLlNlZvqNUFdW7dutbFW2JOpZhPWRSWVC9VbcWXhzTgvQuo4rq46HghJ5Yhngs8IQaOCTXCdE92EtGXMmNG+s0CokNMHPmbMGHucxRCuU9WmTzyYW2+91SrknTp1itQjLoQQQgghYodEtxAiVhD61bVrV6taxwRVWcQ5FVYqqqHcsx1fUNVGHDOCLbDqHTy3m/FsLHaQWh4ouoHbBKzR17179+5I48ty5Mhhyeku6Rxx/t5779liCk4FgvEQ2fSdYzcXQgghhBBXh0S3ECJGgvt/naDLnTu3VWLpIV60aJGJNUe5cuVM1PG4uDqef/55m4GOYA6GyjRj2ugTJ5Wc0V7Bopu+esLUCEHDij5p0iRLIHfVb4Q31XQq3kIIIYQQIn64IZ72K4QIEaZNmxZJcGNpBmY5UxmdMmWKt2DBAuvfJgEbOzLQ303gFpVucXXcdddd3tGjR73z58/b7UD7N+FqVKUR0yTHE3j28ssvW2UbmO3N9TvvvNP/vRFgR6UcIQ48H/eCEEIIIYSIP1TpFkJEC1byrFmzWuo4vcDYkxk35cZcYR+n33jhwoVmP0cgYonG6kwFFrj+wAMP6ChfBdjKsX+z8EFf9caNG218F98FffJY+Ekvh+BK9+LFi60vfPPmzZeN9zp27JgF3VHxZl+knGfOnFnfkRBCCCFEPCDRLYSIFsZDIfYQZogyKtoIO9K0O3fubKItT548loxNRRshxzxqRB4CERhxxfPE1fHwww9bGF3gjO8r4caKuR5u/pJYzgxvF8BWtGhRC7t75ZVX/IKdKrkQQgghhIh7JLqFENFCNRsbOdVURDR/EXKMBcOavGXLFhPhbOOgt7t+/fpeqlSprMeb8VZsS8Vc/Hvo6aZ6jVhmJFhM8P1gNX/yySdNbHPcmzZt6uXKlcv77rvvrE986dKl3uHDh62nm5Rzks+j6hsXQgghhBDXjnq6hRBRgmDesGGDiTjmazsbM1VuRDdi2m1XqVIlE3ZYzbE0d+vWzZ+Ujaj79NNPdZSvkkcffdSOJ86CN954I8btEdxt27b12rRp4xUoUMAuCG5g7Jj73hDjuBSAtgAhhBBCCBE/JI+n/QohkgAEdLkUbARz8uTJTVw3a9bMEq0RaIRuIfgYXUU42rvvvmu93lRNGVVFmBrbILzvu+8+60HGCk2iueP06dMJ+jmTOsOHD7exa8zKxvJPTz2jvOipdwFr8+fPt2Tyzz77zOzl27dvt+uMFXPQD87F4Z7vgtqEEEIIIUTco0q3EGHIb7/95vXo0cMSrbEhYzUmDTtFihQmrKtVq2Yimir1s88+axZn0smxJfOXaiqWZLapXbu2VVB53vHjx606ni5dOq9MmTLWCy6uHRY/Xn/9dTv+adOmte+MZHNGtlHF5njjNGDRhPFhCHDaARj1NnjwYNsHPdu0C1SsWNFu893S/w1p0qTR1ySEEEIIEU+o0i1EmIEwrlGjhrdv3z7vqaee8p5++mkTbw4C0datW2c9xIg3erQnTpxoo6boLSbUizFTpJOTjI3gGzVqlCVik7JNFZyANV4HEQh33HFHAn7i0LKac9m7d699N2vXrjWXAvDdLFmyxKreVMWBhRR669955x0bLcZ4N8cnn3xi3xEQrCaEEEIIIeIHBakJEYIgnPfv3++dPHnSu3TpklVHsX1j+abSiSheuXKl2cGvBFbyJ554wuZtU8mOiIiwfTdu3Niq28yARoSznxMnTngvvviiN3nyZH+lFUGPtXz9+vVeqVKlrtvnDxdY4MCtwDHm+8CxQGhd4Fgxvo+cOXN6JUuW9G666Sa7PnXqVLOgs6AC9OzjYBBCCCGEEHGPRLcQIQTVZ6zfjO369ttvIz2WPn1673/+53+sN/vLL7+MUXA7qI4SzEWvN2KuT58+NsYqderUln79yy+/eGPGjLFZ3FidEXBDhgyxKjrV1XLlytm4qjfffNPEOsJPxB0sdAwbNsyuUwVHSDuHQXQQhocdHfheDx48qO9FCCGEECKekOgWIkSYNWuWBWzRrx0dVD87dOhgfdeIbwLPEOLYzeGPP/6wyvjWrVutot27d2+rWLM9PcCFChUykU1VlH2RSk4llRRserzvv/9+6xsmnI33QxWWUVQkaPN85nmzffXq1a/jkQltWNhgAQXHATzyyCPe22+/7WXIkOGybfluaRXo0qWLP12eBRMC2oQQQgghRPwg0S1ECEBPNWOlAiHIDBFM1RPLMRVQhBZimx7r7t27W+U6sOJN7y9VT0K3CORilBQCjW127txp21DlxppO3zCVbkaE0e/tZnUjxKl28xrsw1VdEfh//fWXiXXszdjWRdywZs0aW8ignxtwEzArnbYAUuhJJ1+9erUJbnrvHTgYuM/1gAshhBBCiLhHoluIJA5V5UaNGvlvY+HGclykSJFI29HDS3AW4hdIwA4W3diTGU+VP39+r0WLFtazTVgaFvHXXnvNxBy929jHV61aZeOrCGFj3BjPI1Gbyiv95GxHWBfPKVu2rI2wciC8CQB78MEHr8sxChfhzXfsKt4xwSIN35mb2y2EEEIIIeIH/WtLiCSGq1oitpmRTe+0o1+/fhZ+Fiy44dy5c2b3jg5s4whlBDcwTooqKZVr7MpOLNObPWLECBPwrVq18gYMGOBlzpzZhP6CBQssRI0KO9syiuqWW26x/m9uly9f3vZDxb19+/Z+i7O4dqpUqWJuhF69etliSVQgsBHmnD98hxLcQgghhBDxj0aGCZFEYMTX+PHjTQBTWQ4ECze92aSFR2cVZhvs3dGxePFim7ntQDBXrVrVrvfv39/r2LGj9W337dvXLsGVchLSb775Zhs3Ro/3xx9/7B06dMhmfxPqhvimdxzxzagrLO8rVqyw6rqIG+iZx5FAewDtBNj+WUxhbBgLIw0bNvSyZs2qwy2EEEIIcR2R6BYiCfDGG29YbzVzl1u3bm2CF/FEONmePXvscVLLEVajR4/22rVrd1k4WvHixb2FCxd6BQsWtCRzxoYFQh82dmMHlnJEG89j5BeCnXC1YLAzYxMnfG3w4MFWHUesU1kvXLiw9Zbzml988YX1DyPMHaSsS3THPSx00HIQ2HYghBBCCCESBtnLhUjkMBubdOnOnTtbsBnCmOoyApzqMlZuxnp99913Xvbs2U2cL1u2zGzhXEg0J+yMEVFUyPlL5ZMxXohzwtMQzljL8+XL53/dNm3aWCW6UqVKVqmmQk21HTEdCPuqXLmyvSfCvOjhZgEAEY79nZFUVNlZIBg4cKDtC1EI7J/Z4UIIIYQQQoQqClITIhGBqCV87NSpU/6+airXL730kvfKK6/E+PyWLVt6J0+etFAtRC/92S4cDUFNry991Iz+woLs7ONXAlFMBd1Z2qlWUzGnCo7IZ0QVfd2klVN9Z143FW/C1t5//30T94wMI8CNxYASJUpYNfz06dO2P9K0EeZCCCGEEEKEIhLdQgSAIP3ss8+sFxlbNtVkrNGBCd9xDRVgxChWayq/gRB0VbRoUe/rr7+O1VgnbOdUxGvWrGn92YjfatWqWQI5jBw50nvuuecsDG3cuHF2/3/+859o94edHDs7lWuSyQlPu+eee7xvvvnGX60OfO3AHm/GV2E7p/8bqzvvCxHO86iGU5kHjrX6jIUQQgghRKiinm4h/q+ijEV7woQJJgKDKV26tPfss896DRo08FKmTBlnx+z333+3cKtgsR0oyEn5LlWqlM3XplpMYFpwvzZV7d27d3vLly/3duzYYQJ5+vTp1lMdGI7WtWtXG92FACbh/PDhw7a/6CrcHTp0MPs5o8GaNGnijR071oQ148QQ4ojn6OB9Y2XnuOXMmdP7/PPPLUCN69jiHenSpbuGIyiEEEIIIUTiRj3dIuxh9BZCEAt3VIIbELtPPPGEVXERt3HB2bNnrQodKLhz5MhhNnJGb9WvX9/EKWKXSjiCHwgiC+7XBirliOxp06aZlZzRYlSzEfWBIn/SpElmQ6fKjfWcRHTudxw8eNCORbZs2UxYc2nevLn1dCPaqVbz/rCV058dFaSd4xrAYs6+mclNXzefZ8uWLd5vv/1m21HFJ9VcCCGEEEKIUEX2chHWTJ482cLEHFi4EaIPPfSQiUESvpl7TXXYgXCkYovt/FpAVCNe3T6ptD/22GMmbgGRjwBmBFdU9m1w/dr0bnM/QWsIbpLG9+/fb1ZueqYdzPSmEk7fN/3jzrKOjfy2226zqjlinesIc0R9YLiag3FgvFe2JeWcIDZeh55xxPqqVatsO+Zz8zmws3/00UdmNWdUGK8PLAC0bdv2mo6jEEIIIYQQiRnZy0XYsnr1aptr7aCaO2jQIKs2B9KjRw8bd4VNml5mgsGo8mLpJjzsaqDa6wQ3feNRiXjs3VeysjN/mcRxBDd88MEH1kPN+ytWrJiFsTVu3DjSc7DPI7orVqzoF9tYx+m/JigNuzghZ4j5W2+9NdrXJiyNeeFU4b/88kuzwQP7+OGHH7xmzZqZYGdcmBP2LGTQE87igfvcPF8IIYQQQohQRqJbhC29evUyCzRgmx4xYkSUYWVUm6ns3nvvvWY/R9QeOXLEtn/ttddi9VqIWnqyEcoEl9Eb7UiTJo2J0+B+bbZ3tm+SxrGh79y50wLICHsjPTywX7t3795WLWd8FwsE9KkH9k7T/03Vnmo5lWlAXCOQYxPSFgx2d+aD06sNVLmHDRtms6EDZ3EDSeWksLOo4SDxXNZyIYQQQggR6sheLsIS0sCZbw0IRFLCEb1YtLFC0xdNzzUzqKlKU3GmKkwlGPs04pfrzM/u2bOnCc6oQKQzYgsbtbNUO3hNBDjW9VdfffUy6zhWcJ67a9cus3hTIUZMt2rVynqpSSh3o8AAsYt9m30QYDZ79myr5OfKlcuqz9xG9Dv4/FT7r1TRjgmq6eXLl/cnkQPHB2s6jgEWGzZs2GDzui9cuODfhrFhGzduvCqxL4QQQgghRFJClW4RlhAw5kDwYht3EEjWsWNHE7iIbYQv1mhs12XLlvU2bdrk7d2716zaiGlmVlOFZpSWA4EeERFh1XSquYhQrN6kiiNEeT2qxFizqTZHNZIMwU6lmve6Z88eq2xjc8+dO7dVqrGWB/ZbI/4R6gSWEfaGqCckLSrq1q1rtvJrrTRjr1+/fr1Xr149q7679x1Y0Q4EkU14HM4BPhcWdCGEEEIIIUIanxBhSO7cuX2c/jfffLPv7NmzvpYtW/p27NhhjxUtWtQee/LJJ31VqlSJ9LzixYtHun3gwAFfvnz5fFmzZvUdPXrUf3+vXr1sHz179vT9/vvv0b6Pbdu2+fLnz+9LkSKFb/Hixf77z5w54ytWrJjt59Zbb/Xt3r3b7n/88cd9mzZtuuJn+/TTT33JkiWz1w+8pEyZ0j7Txo0bfXHN33//7Vu2bJnv0UcfjfK1b7/9dl/Xrl3tc/zzzz92ne3mzZsX5+9FCCGEEEKIxITs5SIsSZ8+vYV+0UNN1dolg2MTz5Ili9m+SfCmR5rQtEBbNJXuQEgML168uFXF58+fb3ZwErupdLPPmHDvg6ozFWrs3tjb6SVnnjbVdVLClyxZYrbxwCT1YLDI0xNOkBp94PSfU1mnYs57DOzxji9431jK+VwcxwwZMlifeWBVneA1eskJkyPVvFKlSvH+voQQQgghhEgIZC8XYYkby+WC1Bz0czNuC7s3lm5EMyIWyzlCGEGNsGbcF0FgzJ8GhPk777xjohxxScDZ888/H6v3ki5dOq9KlSreokWLLOisffv23ty5c61fm5A1rOukkiO+Ec70UQenpmPpxu4+cuRIE9yIWdLBE4Ls2bPb5Upgfed4MZKNY4U1/VpHsAkhhBBCCJEYUaVbhCWIasLQ6H8+fPiw9UN369bN+qsZ1cVjCG56rgkJQxDSh034GH3JmzdvtjnYhJRRSS5atKjNnyYhHJYuXWqinATy4IA2ePHFF02405fNY7/99psFsyHAGenFSK7Aijrp3wh6UtN5/Vq1allgGYsGVOpXrlxpFXJmXtOjfqVRY4kJPjdVcJLW6VePSawLIYQQQgiR1JDoFmEJAWdDhgyx6wSgMZqL6jGCl8RvZnAjZjt06GChYFSZsUojhPlLenjmzJn9oWmIbqrQs2bNMuHIfgg76969uz+VvECBAibcEekVKlQw4e6EPsITYY+9nGR1BHYwvA5juqhiU6nHto1IJ5yN5HKs59eSRJ5QsPDgji/jx7D+CyGEEEIIESrIXi7CElK+hw4dakL23LlzJnbpQyalnCo0467o80bMQosWLbxXXnnFS506tfV5O8ENo0aNMjFMnzK2aQQ1AjJ4jBhVcXqzqYZjGwfGZr3++uv+5/E+qKAHi27eZ79+/UxwuzFnVNETykIel5AKT6W+XLlyNgaNZHPX/83YNsaN0cfOwgiLCixgNGnSxHruhRBCCCGESOzckNBvQIiEAFGNwHNBaIy8wlYOiOdgC7Qb24UoZhY2IhgIAaMfuXfv3v7+cARxVFDRzp8/v1ekSBF/v/elS5dMcIOr8LII4OC9IEjpGUf0O+jfDgXB7ciTJ4+3fPlycwiw0LFv3z6bgc7iBgskn3zyidn8P/30Uxvldvfdd9u4MRYohBBCCCGESMyo0i3CFirM2JlJ2V69erWFkQG2cqrSDkQxleuKFStadRkhSLWaxG2EOHOzsZbfe++9tu37779vNnLEYVTCHXt51apVLaCNPm5XIT958qTf+k5FG2HP6/3www+R3jeVdazkoQbVfULqWJwoVKiQVbVJbydYjv51B33tzEdnfjpV8MWLF5tLQAghhBBCiMSIKt0ibKG6yhguVzFGENMrjZijskwVu127dt68efOsEo4IpoqNtRn7M89jvBiVaYLO2C5FihQmqqnaIrJd9RtRzXMJOOO5bMP+SpYsadVbEtP5C4SKEcS2bNmySIKb6i6v0aVLFy9UYUGDY4joZsGB6n6g4AaOOSF1JMkXLlzYFi+2bNmSYO9ZCCGEEEKIK6EgNRH2kFTeuHFjC0cDhDdV78C+bYeb501fsYPKLFXuMWPG2LxtxODUqVO9l156ycQ1lXAs0lSyqWBT2W7durUFth0/ftz6xRHuvCbbMxLM2dcR64wTo2pOYjlp66EKn5njSvUfyz798zHBceP44FbAfu5GwQkhhBBCCJFYkOgW4v8EH5XmESNGWIWZaipiDuFHRbx58+bWcx0sun/55RdL3j527JhXt25ds5Fz/cyZM9avPX78eJuxHRPYqgljo7qObZ39Qtq0aU2EhgP0bVerVs3Lly+fjXFzo9YYrcZCxIULF7yIiAiz+TPHvE+fPvb9MIqtdOnS9r3hUBBCCCGEECIxIXu5EJ5nVegVK1bYXG4EOD3cVKypuPbo0cOs3QhuJ4Ydo0ePtgo1Y8K4jiikGk1fOAI8NgnbiP0nnnjCLOzM5+a1GQPGJVwEN7z11lsWNLd27VpbgHDMnj3b7qNXfuDAgXYfPfGErgGz04sVK2bPF0IIIYQQIrEh0S3CHnqHEW70cmP1xqaMaGacF4FoBHfRQ4w4ptd6zZo1NtaKCmz//v3NUs5tBDYBbAh1hCLVWXrCCfpyvd2BYC3n+Q8//LBVy2fOnOklS5YsLL8Pjg/HqU2bNrbYEAiLEMDIMOcwwHXg7ueYtWrVyvroqYYLIYQQQgiRmAjdBlEhYsGBAwfM0sxMbZLMsZUHw2PM7sb+fOLECUvUBmzQhHxhg3Zjwt555x1v2rRpZjF3gr5OnTq23eOPP+5lyJDBRpMh5hctWmTCkX7tIUOG+EVkOIIrgDA5RrlFBZby77//3ps+fXqUj/M8HArY+jNlyhTP71YIIYQQQojYI9EtwhoCzRDMVKqDK6zBMC+bPm2C06iCb9q0yZLIA8GCXqpUKa9cuXL+Ci5jrXgufdvY0xHXiHtGlrVs2TKk5m1fKy5ALph169aZjb927dreQw89FO3zwtUpIIQQQgghEi+yl4uwALFM3zYBXdiUgbRyrOL9+vXzatasaXOhXYI54VxYzrkglmH37t1e5cqVTTj/+uuv1mMcLPyohhOERsUVCFdr1KiR9/bbb9v4L+5nJNjmzZutYi7B/b9wHFiM2L9//2XHlO8O+H64RAXPI+md4DkhhBBCCCESE0ovFyELgm39+vUWsMWoLsZxOfLmzevdcccd3r59+6x/+48//vC6d+/uTyYvUKCAWcB5ToUKFfwimSo3PdhUqtn/woULbQQY25JUvm3bNv9rFClSxCq0sRl9JTybf868bXrkOY6E0+EEwCngXAOvvfaafR/01zPDG8cBCyNHjx41uz/fhxBCCCGEEIkJ2ctFSLJ161YL10IMRwVhaQjurl27mkU82CZOINr58+dNjKdJk8buYxa3Sy/PmjWr9XJjJY8Kqt0IQAnu2NOhQwc7brgMSHF3MM88GBwHXICFFYQ4c9KFEEIIIYRIbEh0i5ADize9v2fPnvXfR1UbgZwiRQqrim7YsMEqp9EFd1HRZnwV20yZMsXuo5eYmdsvv/yyVWGjgtdAPDZt2jSsxn3FBQjnwoULm6OAULvYWMUJYCOIjio3gXhCCCGEEEIkNiS6RUhBZTtQcCPisI0z9xnB7SBVvFChQlEGdyHkxo0b5+3Zs8fs5cyErlGjhte7d28T4FRYSSIH+rVJJEfUs02JEiWu46cNLQhBo0+e8WksbjCeLWPGjNFu/9NPP9niCI4FFlHo6RZCCCGEECKxIdEtQgJs4PT+Pv/88zarOXny5DbL+amnnrIws0DB7aziCLrg4C5AvJFonjJlSqtWI7wR51wQ1+DEOgnkd99993X6lKEPvfarV6/2HnnkEatekwaPc4D7HSyG0D8/depUG7/GqDGC7YQQQgghhEiMKEhNxCmI0bVr13qTJk2yvulz5855t912m1e8eHETTwULFvxX+0NQUbUmtRohHZXYxu49efJkE170BBPERdWUtHCs5iRjt2nTxhs4cKCXKlUq/3Pp5541a5Z35MgRE+YuvAuhTtI54WuEpDFWjL5i+sQ7duxo72PXrl3W901ft4h7jh8/7o0dO9a+VyrafC+cRyTPM1s9Xbp09p3yvdC7v3PnTu+LL77w7rnnHn0dQgghhBAiUSHRLSIJZnppETrYrxGxBIwheJ544gnvsccei1L4OubMmeP179/fBClVSnp0eT7hY8ypRkhx35AhQ8xCHB1sx3ugkolwdvA+EFjt2rXzMmfO7P38889mL96+fbuJYcQyQpj3jT2ZzzBv3jxLuh4xYoSNpEKUvfTSS179+vWtF5hkc94rfd6fffaZ2dFjgsUEnjNjxgyvefPmOoPiESrZLH7s2LHDbP8E05Esz/eHGwEYw8ZcdBwOX375pdn9hRBCCCGESCxIdAvj3Xff9YYOHWoCNjqwUSNusXAHh4S9+uqrXp8+fWze9XPPPWd9z1SbHcxaXrx4sTds2DDru+b1EE6BUMUkRGvmzJlW4Y7K9s2F6rPr36UC/tFHH3klS5aM9FoI/cARYFS/EeN58uQxIcdoKmD8F7O5EXPM4Y4Jnstz2JaKKxZ0kfAcPHjQK1OmjJ2jLLIEp9ELIYQQQgiRUKinOwyr2VR0EcDYdhHGWHOZQx0I9yNEsW87AUw1uFevXt6aNWusguzGYRE6huAeMGCA17dv30hi24FIJ8yMajmzl5nJTPW7SpUq9jjvBTGLhTsQLMVUODNlymTWccQU4hmhzf2I6eAALV7rzjvvvKyHGzt59erVvZw5c/rvp5pOoBrjwejvplIeHSwKNGzY0Czl9B1LcCcecEEsX77cnBSE2y1atOiKrgwhhBBCCCGuF6p0hyCMuaLahw0aoUg1GFFCNRCBjMjOli2bXdh27969JnpvvPFGE6T9+vWzdG5EJVXljz/+2GzYS5YssdtufBbp0ghfqov0PbNNbKASjcA+fPiwvUdswSSEk0DthHbp0qW9VatWmYjq1q2bVdADRRQi+Z133rGqOs9/4403zC5OsBl2ZD4zvdx8Fp5Xq1Yt79ChQ7bogCDntZ1YJ7SLRQfEfIsWLbxnnnnGK1KkiP+12HbixInWp87rzp8/P9IcaZF4YDGGlgPOR8LWoloAEkIIIYQQ4rriEyHDTz/95BsyZIgvR44cRGtHuiRLlswudevW9X388ce+f/75x/+8v/76y7dkyRJf9erVbdtWrVr5Ll26dNn+161b50uTJo1/nwMGDPBFRET4kidP7itevLivQoUKvsaNG/suXrzomzNnjq9MmTK+qlWr+g4fPmzP37Vrl23D/aNGjbJ9rFy50jdo0CD/PjNnzuwbNmyYXe/SpYvv77//jvbz8joHDx70Zc2a1XfTTTf5Fi1a5KtSpYp9tg0bNvjy5s3r27Fjh69mzZp2/cSJE778+fP7XxfOnTvnK1iwoG/MmDF2P5+Fv1myZLH7c+XK5bvhhht8qVOn9j377LO+PXv2xMt3J+KOqVOn2nf46quv6rAKIYQQQogER5XuEIGUbnqk6WUOhmofIWJYwqkYXwnCwagSMqqJ6m5wpZCgNfq1sZxTMaaiXLRoUevDJtgK+zlJ5VScSTH/+uuvvenTp3sTJkzw6tWrZ33jBF0x05pUcqrt9FcfO3bMKs88h8fq1Kljz4tNpZKQN6r3p06dMvs6VW/3/rDSE75GFZ+QLT4/tnb60nkvHJNNmzbZ51q/fr3tb9CgQVbRpvLN56MPHFu5s9OLxA9J9aTacw5xfjg4R8gTYOyYc4Hkzp3ba9q0qXfXXXcl6HsWQgghhBChiZoeQ4APP/zQq127ttm2Hdi36aHeuHGjN2XKlCsK7sC0b/qVSQ5HdCNYsaVj46anml7u3r17e2nSpDHxgiWdC6OdXJI04h7LeP78+e06qdKEmQHCGgELjHxCvI8ePdrGijlbebVq1exzkFaNvZ39INKxm2NzxyKeJUsWm7uNiCYkDfv3/fffb/3gLD4Qdla+fHmzvpN0fvr0aRstRdAWYgtx5RYn5s6da+PCEOCu95t0c9mSkzZkC3CusIDEucvM9pEjR1rCPudHMD169LCWCvr+A0P5hBBCCCGEuFYiJ1CJJAfjuRALTnDTn4ywXLFihc0xZnYxfdGki1esWNECzNgWscnYLkQuo7eWLVtmvdmMwBozZozNs2YsF1VfLoDgRuBTDQ/EhZbRM05PLYIXAe2gbxxcPzjQb41Qd4Ib6MHldQlXo3KOMKdPnLFhvAcep5+aoDTGRLFfQtn4zLw2Ih0BTuWdz4aoR5gjshFVLChwP6/NgoB7zVdeecX/HujnluBO+vAdkl/AYhKLTSVKlLDzNirBDfwmqICXKlXKGzVq1HV/v0IIIYQQInSR6E7ivPbaa37hir2cVHLsskClGts1QpIK8Lp167wcOXLYNsytJmwNGy5zsxHOVI0Ru9wmfIzKNsKY/QCWbarcVNGxazsQMlSVsfFSFWdf3Ha4bQNTxqmu8xqBApdqNRVoQs/4TLwWr0+FEqHNtsWKFbPqPXZgRDWiHDGP2GaRgaA0RDZhWjwOVDr37dtndmIq82zLAgPBbSwQcIyA44aIF6EB5wwBfZyfbsEnbdq01lpAKwHfO4tSLMhQCQfOH4L7nPNBCCGEEEKIa0WiOwmDgMQu64TtiRMnTPi6Snbjxo3Nho2tGvFKBZvqX//+/SPZv91sbteznDdvXqsiM54LwUslHNgXlt2TJ0/6RQxCmORvXoseWp6LhZwK/MWLF03UMJIrWPyyTyryzpYO9HgDAohRYFjYEUHcz2dif1jMgYo3Ipt9UKFktBl92Ih1XhvLO+IKqPJjl2exgEom282aNcss61jqAaFOFZ8eXxEakA+AoHbQ5sDYu+HDh9t5TysBvw0E9pEjR2zsnaNnz57mFhFCCCGEEOJakehOwlBVRohCx44drXoXWMmmynfPPff47dMIDUaCIWqjsn87CEFr1qyZ3f/NN994+fLls/vpvyaULNCCjVCPiIiwqjEWcKrLjOyiN5brCBknZgLFLwLovffeMwu7w4l7Kup33HGHvW779u1NuPN+uY4wpkrNQgFVacLeOAZsH/ieOnfubFZ1QFxRref4MIYMIf/tt9/6X5eFAl7DOQREaDB48GD7rl1rBOdp4CJPILQmBLZSuOcLIYQQQghxrUh0J2EQio6nnnrqsiAz7LUIUAQqdm9ss1T5qFhHZf+GhQsXmuglbRxxi2h3Nm1SyVevXm0Bba7SXaBAAQtIo+KOXZ1Lo0aN7ML7w9aeNWvWSOKX++nBRiwHhrvRD45w/v777+021W4WFgg6Y7GA68wHp9I+bNgwez9U73kOFUv60vlsfPaMGTNedryoXlIFd0KMPt+pU6daABuvJUIHZs+TUu9+EywYuYUW2jAqVapkrgdaLgCXCOcWizo4MoDHAhdnhBBCCCGEuBqUXp6EIUzMQYXbBZnRk431HAGK8KCa7OzfiEss1M7+zbgs7N+EkVGtRkwjzAlnI8CMHljSz4H9sj/XA+2ELJV0KuCEliHUY4KebCqPLVq0sIUAhDOvS4Wd94xAQkBjHWcRoGDBgvZZsZNjXUc083m4joAiKI4qNdbyTp06mWhnMcBx4cIF78UXX7TXQeS7YC1EuwhNJk2aFOkc5bfhmD17tgnxAwcOeG3btrWWBSzmWNFZYHIJ+4CTgvNGCCGEEEKIq0WV7iQMlWwHY7GCg8wQzsePH7c+Vmf/RqxyO9j+vXz5cgsuy5Url6U8796928QtFWxn00a8IJLptQZmbBNYhjAGQsm2bdsW7ftlf0uXLvUeeOABr0iRIjZPm30QnAYEXvEYPdcIHTcrG2s5CwQEsfF4v379bB43iwjY6N18bXrFqazzPrHGI765TqWd16KffcmSJeYKkOAObVxOQaALxOGS6zlfCA90C0G0YxDuxznmoL3iamEhicWv/fv3mzsjuI1DCCGEEEKECT6RZGnatCk+abuULFnS9/HHH9v9Fy9e9JUuXdr3559/+vLly+fLkiWL/zk//PCDr379+lfc7z///OMrWrSo79FHH41035w5c3wpU6b0v+bo0aP9jx88eNB333332f1Vq1b1zZs3z/fjjz/6zp075zt69KhvwoQJvsKFC9vjNWrU8P3+++/+527cuNF30003+fd7xx13+HLmzOk7ceJErI7Dt99+67v11lt9N954o38f7nL77bf7unbt6vvuu+/+1bEVSZsyZcr4z4G//vrL17JlS9+OHTv8j1eoUMGXIUMG38qVK+02vx/HSy+95D+XOGf/LUeOHPG9/PLLvkyZMkU6F3m9Pn36+A4dOhRHn1IIIYQQQiQFVOlOwtSrV89/nQpzVEFm9DuTzDx9+nQLLmMON33VVPMC52YHQmI5fc7t2rWzXte3337bK1mypNewYUOzagMjuwhvc1CxxqpOKjgVa/q+M2TIYAnoWM47dOjgZc+e3RKhqXYHpoSzb3qrXRAalXTGejFTm/70K0GVm89JNZxeXDeWjD51quVU9fk89KaL8CEwMI1k+2Do16a6TduBc42434ObAQ/O5REbeA6J+ZzntGjUrl3bfovY13GD8JugdYJWEIL+yE4QQgghhBBhQEKrfnH1UNHOnDmzVdFuuOGGSJW8wAp169atfcmTJ/dNnTr1ivtj2+HDh19WLQ6+tGjRwqroV2Lnzp2+hQsX+mbMmOH74IMPrBIeE3Pnzo1USafamCxZMl/NmjV9K1as8F24cMHe49mzZ33vvfeeVSvZrly5cr5Tp07Ze2rWrJnd161bt1gcQRGqcI6682jBggX+SjfnD78bOH36tK98+fJ2vUuXLr7Vq1f7Ll265MufP38kBwnnVkxQTX/88cftdzh48GDfr7/+GuV2v/32my8iIsLO7Tp16tjrCSGEEEKI0EaiO4kzYMAAv0DIli2bb+/evZdtwz/s27dvb9uULVvWN3PmTBOwDkTsxIkTfQULFryi2C5WrJhv2rRpJlziC+zv3bt396VLly6S+HbXETXueqVKlXzvv/++X0TB33//bfb5VKlSxUosidCERRp3nqRPn96s3rRcjBs3zs4bLgjudevW2fbHjh3zPfDAA2ZLL1KkSKTznnOObYcOHWqLSVGd/5yzbIfAjw1Lly618xqxL4QQQgghQptk/Cehq+3i6jl79qyNP9qyZYvdZgY3FleSvu+6665IM7DdbGwCyrDfErhGcBRha9jGSfUmDZwAsv/+978WxoY9HOs4SePYwJ0FPL7hPRLohi0d+zj2cKzj/GUmd9GiRW0EWVQQXpUlSxabC85nFuEHVnHmrzM6DNasWeMffXclSOBnnj3/WySA7/PPP7fEfgL4mPHOeUnYIOF/XEjOx47Otn379rVLbGHsHSn+hw4dijSvXgghhBBChBYS3SEAopkebYRyYEJzxYoVvXTp0lkCOD2sCAYHo8DoMeUvvdf0h9NrmphgFNlrr71m481IYufzffXVV5Y4zXgx+mXpDSexnX5uUqp37Nhhgov7GXnG2DHX5y3CC/qnyTZwvdmk9JNLEN1M9q+//tqrUaOGTQKAwYMHW/q9g98P4h0BTi4BWQlkCbAoxXnGIhALVu4cZQGLfAIWtCIiIuz3SD4C+QWXLl2yzAJGknXv3t2S+IUQQgghRIiS0KV2ETecOXPGbNX0QMfUk129evVYJ4MnJNh8mzdvbvbxkydP+vtyjx8/7qtSpYrZfDds2ODr0KGDbb9v3z77+/PPP/vy5s1rn3XLli0J/CnE9YbzhaR9LORRnf+lSpWyvAHXT71582ZfmzZtfClSpPBvw/l1pdwCzj3OLdo7br75Zjs3A89RcM+nZaJatWqR7gPeHxkE5DLQFiGEEEIIIUITlQBDBGZsz5s3zypuVM6wYAdy++23e506dbJq8YcffmjV7cQONvGcOXNasjRWeMfBgwe9AgUKmNW9WLFilsYO2H4hRYoUXsqUKf37EOHDjz/+aPZwKto4HmbPnm0zsqlS8xgp/qSM161b11wgJOszs33KlCmWug9UpEkbd/O8o4JzjxYH2hfY94MPPhjpHI1uHri7j+RyXCbVq1f3jh07Zq0cQgghhBAiNJHoDjGwztIrisDA2orFGuGJZZYRWvnz5/dC4TMyngyRxDgm+tUD6dWrl/fkk08m2PsTCQPnO4IZ2ze92bRUNG7c2MQwizBkHDzxxBNmI//0009trBy/EwdWcUT0Rx99ZItUsc1UgMAReIHwfhDkjzzyiP++IUOGeHny5LH3QwaDE+ZCCCGEECI0kegOUehj5h/09GlT9UZgJDUQSwcOHLjsfj4PAWsPPfSQVe3z5cvnf4yeWaqIRYoU8e9DhAfMjifUDOdD6dKlr7gt4YOEpNHrze9kwoQJNtOd3mucErElderU9je6SnXwPHCgT5yAN7IYNm/e7Bf8QgghhBAiNJHoFokWwuGwzP/yyy+XPUaa+tq1a80m7FKpqXrPnz/fArQQ31jTCxcunADvXFxvtm7daunib731VrRBacEQfDZ27FhzgXAORVetvhJMAaBqTcJ5IKSfE5YG7Nft21nYWQRjMgBV9+zZs0t0CyGEEEKEMMkT+g0IERUkQCOcSX6mD3fFihXetm3bvO+++85SyrlNry6C5c0337TncD+97VQxsRcPHTpUyeVhwrhx48zVMHz4cGujoFeb82bRokXeyJEjTRxPmzbNRskB/d70Wbdr184q3ePHj/dGjBhxVa/99NNPW0vDiRMnLDOBc7Rly5Y2ng/oISeF3yWiY23HjUE1nnOX5PLrNYpPCCGEEEJcfzQyTCQ6qP5Refzjjz/sNoFTWHRz584d43MRU/TxLlu2zOYfu55ZEbpQUWaxhZaDQYMGmcBGBBOQxmg5HBGcU4hwbOQwa9YsmwPPbHrOk8mTJ1vF+2rEL5kCiHzGkyGqYwtim1ny9JBXrVo1Vs/Blk7POuFt9J2T0cDvQwghhBBCJF5kLxeJCnpya9Wq5RfcFSpUsJRprObM674SFy9etAA1LOmIKgnu8ADRy/nCuYLgdinhVJwRpVwn0Xz79u3+yvPcuXMt4RxoQeC8c+fcv4XzE5FPQBrV9Njw7rvv2gIBz3344YdthjiujqhAYDOL/v777zcre5UqVSyYjc+UMWNGr3nz5t4XX3xhlnYhhBBCCJH4kOgWiYqpU6f6E6VJfqZPd82aNRZYReWybdu23pYtWyI9hwolFc177rnHxDZW4Tp16iTQJxDXG9cn7QLQGClH9bh8+fKReqUR28A58vjjj/tbD9x4Obefq6Fv375mVWfRp0ePHhaSFhWc27179/aaNWtmuQRU2XkuEwcYQUZbRCDkFNBC0apVK6vWR7XQxOdBgFerVs0S3IUQQgghROJCPd0i0YA1nN5cB9VHqtX0d5M03ahRI7MEM1OZ3lxmjWO1RchgC3aVvqiC10To4uzV9PiTIs5YMCrDiOzAVHHCy7hvzpw53uLFi+1ccs+71gRxzj+s69myZTOLOX3k9erVsyo2C0aMBGMBiaA/3gfW8n79+pnw5y/btmnTxsRz586dzXZOMn+TJk2s/9uBMMf1QUWfHnJcHW5kHotTZcuWtcR0KuBCCCGEECJxoJ5ukWj45JNPrFoHWGjff/99r3v37jY7mTFhjIQiJXrUqFFmz8Vui9Bm3jH2WkQHkF5N76sIH0qUKGFBaojf559/3s4jer1xS9DTzVx3rN8I3Nq1a9v5xIgwRDgVcgQ328UFLPrQP47jIrAlAicGoWtUw6Pqw+a9kLyP1Zx+bRwcLgEd8d6/f3+vVKlSkfrOsZ6ziIC93VXXcYQwNs1Z7YUQQgghRMIi0S0SDQgOwqiAanbr1q1NoCC6z507582cOdNGPFH5o+K3Y8cO25ZZ3myDiKIqDvTnSnSEDyTdUykmUM2NiSNYzZ1XWMgR3VmzZvU/h2r4nj17LFmcBR7X4x2XcK5S5WZk2E033RSr57BghHB2VXqS0Pk9UCGPDiz1LDBgV4c33njDe/bZZ+PoUwghhBBCiGtBPd0i0RBoBaZaSJ/r5s2bbfYyYnrDhg3Wd0ufq7PUBkLF2/Hrr79et/ctEh4S6xHc1atXN8cDY7loR+CCCwIXRaDgBmzo3377rVmx4ysDIHny5FbVjq3gdr8D91tgrNmkSZOuKLiBvm+s6w5+MwpWE0IIIYRIHKinWyQaSJkGBAYWYXph6fOmdxWRjYDJmTOnjVfKly/fZc8PTJ9OlSrVdX3vImHh+yYPgB5oev2Zue2C0qKrQD/zzDPe0qVLvYULF/rPvcQAgtlBlRzRzqITArx+/foWlkbSeUREhFW3BwwYYP3fzsL+/fff27xweruZWS+EEEIIIRIWVbpFooDKHinM9KvSj7tkyRILUmvQoIFVLg8fPuy99NJL9jhW4OBxYPS+uuRn+nMR7CK8oMr90EMPmZ2cc2j58uW2aBPcN02IGpkBWNKZz/3YY495iQmS191CAuc+vwHH7Nmzrfec38DAgQP9FXtEOSI7cOHJ7UcIIYQQQiQsqnSLBIeqXc2aNa0fdfXq1SaIgFnE27Zts3nLTz31lFXvqOTRq82cZey0iG36V5nBTN83MI4pMGxKhD709deoUcPbvXu33SZIDIs5lnKEOIswLOysWLHCO3bsmFW2aV9gFFdiw439ypEjhzk7AnEVeSrgVL4hV65c9hcLe6DDQ+PDhBBCCCESBxLdIsEhUZoZxPTdlilTxn8/lcpACFUDqpcI6+bNm1vo1IMPPmjp1QgRwDYswgdGxrFQg/AG2hAYwYW1GvfDN998Y64Jkr5/+ukn/3xrUsKZ5U0KfmIkuoUjLOVYyElID4RUf8aJ8Zir6hMuyOcmDZ3Edi1GCSGEEEJcf5ReLhIUqtN33323VbKHDh0a6+dRtcycObMlVGOr3bhxo92P+EK8i/ABoYlDAnBCsFjD2LioIGCPcDVGzwEhaiSYky6eWGDWN+0Ut9xyiy0okEJOOr+rbAOuEEafbd261W8lZ0QZiw3YzYFRaAQPOjgm/F6o7qdLly4BPpkQQgghRHiinm6RoLz33nsmoJnBTcUR8UOiNLz++uteuXLlbEaxm0HsKt2lS5e2sWH07zrBjYCiT1eED4hOJ7hZvKEHOjrBDVR86emmWgyE9DHzPbEtIrgFqcD3Rhq5m9vN78QtFDA6b9CgQd6bb77pPffcc3YfVW1m3C9btswWoebNm2e/md69e9txGjlypNLNhRBCCCGuE6p0iwSFfltAHDAmDKFAVQ/RgBBHUGE9Z6ayS3UmcG3GjBk2j5m+XKDqTYJzoUKFEvTziOtLu3btLAwN6H/OkiWLiUqs14sWLTJxSQYAM7p57JVXXrGq8OnTpy0jADhnyA5ILNbrTZs2eSVLlrTrCGv60RkJxrxuFqmcdZz54hUqVLDQOPrUqX6Tj0CKO60YUSWynzx50hs8eLBZ0Xv27GnXhRBCCCFE/CLRLRKMLVu2WOL0448/blU6QCwguqnyzZw50xs7dqyNd6KqTUUPscHYJBKnGSOGSKeCN2fOnMvmMIvQBoFJij2J3Vix9+/fb7Pae/Xq5RUvXtycEiR9s2iDCJ8wYYL1ciNGOacQs+zDCV2ek1goVaqU38HRpk0bb+LEidGOQKMCTlsFM+3Xr19vi1ExwWIEVXH2y8KFEEIIIYSIP2QvF9cdAs/q1KljIofqdlRiAoswQoie1I8//tgEtqtyI9J5jqtMdurUSYI7DDly5Ih/RBbJ5QhuQFSTeJ8/f367TosC6fbuMeC8CkwGd+FjiQUWDEgjhylTptgiE7+HYPgcL7/8si0usEgVG8EN3bp1s97v/v37+y3rQgghhBAifpDoFtcVRDap0vTVApVrbLHBYC8n9An7ObbxfPny2bZUtBs3buzfFwTP7BbhgUurh7Rp09rfgwcPmn2cVHLmtTs4dxxdu3b18uTJ4+XNmzfKfSUGeP+0UNx4443+9gss5+Qe0JdND3fHjh1tsYnrtFcMGTLEetWbNGliQrpFixbenXfeaSP1HNjr2YZ98Rxs6R988EECflIhhBBCiNBHI8PEdQNLL+nKLnEZoVS1alX7Rz8pzRkyZIi0PaKBC/OWEeGEXnF59NFHbRQS4WrYiulrFeFHYOI4YXxcqN7S/4/I5rbDiVegnxmBivCOal+JBVLW06RJY+Px6EEHrPJcAsHx0b59e69Hjx7Wv469nkUtPiO/r7Nnz/q3ZRtGpfFbpJe9bNmyZrunZUMIIYQQQsQPEt3iukGCMunSgIhm1BdCmyoeFlr6UQm0whrMCLEVK1ZY8BMhUvR8p0qVym+xnTRpkllk6UflfhF+EJiGXZw+bUaA0XaA1ZoKNpXeXbt22WOcMy5gDzs2o7S4BFa3c+XK5SVGSO6nej979mz7DfD7CITFBBYY6tata4IbOCa0X1DJDibQXk8LB1kJU6dOvU6fRgghhBAiPFGQmrhuYGv97LPP7DqWcULUoG3btt7ChQu9L7/80uYsxwYC1Oh73b17d6yfI0IPKtv0MgOuhxIlSth1WhOAkXIpU6a09HKs2E8//bSdM7QmfPPNN7YNrQskmSeW9PLoIDCNBSlcHvSyUwX/6aefrEq9b98+WzhAoNN+wYIWPeFU/al0Y0UPtNfTpsH8b7YhBd21agghhBBCiLhHPd3iukDyuBPciOR+/fr5Z3JHRER4yZMn9woWLGi9rG4md2D/KSnmTnjw3OHDh5tNWII7vEE4OpjTPn/+fGtXOHXqlNmrCVCjPYFKN/kAVI5xUARWgZ955plEL7iB98gCASPCatWqZW0VjEGLyl7vQtiigt8NSe8sdCHgSXHfu3evCXqOmxBCCCGEiFtkLxfXBcKtHAgCLvR0Dx061EYi5c6d2/v1119t7BHiiHnEgf2nCPLbb7/dQqHoaWW+cOfOnfXthTmM1mJk3FdffWXV3vvuu89s44wCq127tmUC0H5w5swZE+NkCrDY4/qcOQfZJqnCohOV/KVLl3pffPGF314fHYH2eo4LkwEIUwvsbyesjcWMhg0b2r6FEEIIIcS1IXu5iHeooNFziq2XntLKlSub2Pn888/NJktIFJVHelYR1MOGDfMLbcZAMbObvlzuo1d10aJFVukTArBUI77p/4cXX3zR7NT0fAdDiN/AgQPtHOJcWrVqlYWNJWVat25tCwq4QPjNOHs9/d/cT883I9WYze3s9fS6M0oNxwgCu0GDBnY8qHxjxadHntwFrPu4A4QQQgghxNUj0S3iFSzlzBimSk3wWcuWLa0XFZ588kmvS5cuVt3mMaqPWH0RRPRrE7rGhXnMiHUq4XD+/HlV4EQksFW/++673ty5c62afSUQpy+88II3YsQI2x7BmZRhQYoWDEaMNW/ePFbP4RhwnOhr37NnT6R0d+A+er8R37hOkvoxEkIIIYRISCS6RbxBPy092sWLFzchjfAOBNGN+MESTB8qVnIqbffee6+3c+dO/3aIcSrjVLqxxFL5FiJQIGKzRkSzePNvROeWLVusvzlYdCY1CE9jTBgimRyEmD47s77peSdQjdT3qOD3hvV+wYIF5krhdyyEEEIIIf49ClIT8Qap5CQqY3ENFtzBIMAPHz5sVtbvv//e+/nnn63/FBASCAB44IEH9I2JSIwfP9677bbbrCKL4GzSpImNDKOKzRxqgsdwSwDnFOPouI9z7NChQ97y5cuT/BFl0YrPSh4CNnLnCgmG3xbVcAQ3bpLoBDcQbsg4MX7DJJwLIYQQQoirQ5VuES/Qm00gE0KIf9wzkomwK6ra3CaZnH5ubnfq1MlmdRMEhbhmDBLb0AtO/yl9uCQzOwGOsBACCEwjD4BKL+cMs6p79eplVVlur1271s7F6dOnexMmTLAeZkLDCFkDztH06dPbCLukDgsKhAsy854AtGbNmlnIHO4QchPmzZtn7RqIaTejm+NCkCGfnxA2N1qMhQkmDvzzzz9mXR83bpx34MABf1q6EEIIIYSIPap0iziHqmK7du3sH+iIGwS16wk9ceKE3abKSL83AVhUufn71ltveT/++KNVKvlHPsFq/IPfCW4SzlXpFoGQWE4yOecMghvo/2fBJn/+/HadwDBaHYBxYevXr7cwP0Q4gXz0RIcCJJLzmajeM8ceIU3IGgsSLGwxu5uQOQLnWMzKkSOHWdKpejO2LxC2YxGMffCXY/v2228n2GcTQgghhEjKSHSLOBVA9Mnyj3l6sgm3ouJ25513RkqaLlCggM0cLlasmH92d6AYSpcunfXpEsBGZRLYnvnCJCwL4XALMi6cj/OL8XRkCWA5d5DgDbQwUN1mVBbBa4H7CBWo/FO1RnyTUs7ne/XVV21UGgKbfARgQYLfU+DMcgeWcifkqYzjHECoCyGEEEKIf48UjIgTNmzYYFbWhQsXmsDhkilTpsu2w9ZKZRErLMKHinewGKKyBoG9tlTBH330UX1bIhK33HKL/aVVAXHJQg/9zSz0BIppF5SGOGdEGEKSHmiEqdtHKHLTTTd5qVOntsT/wM/pFidiGr2HVZ8qeeBscyGEEEII8e+Q6BbXzK5du7zq1at7p06dstsZM2a0f+y78LNAmP3LDGH6srGu5suX7zIx5GYNA4KBhGXmCwsRTLZs2axi++mnn5qNmgpv3rx5rW+b85JKL4s4hQoVsu2xmjO/GhiXhTuDbUMdqv4sQpBcHrg4we80OrCT8xsmeI1gtpjCEIUQQgghRNRIdItrgn/EI3Z++eUXu409HLGDGCI8LSoYQ0TAVd26dW37YDFEcBpgiaUCfqWEZRHesFjD+cG4MNwWgwYNsnOKMVfMmeY6o+i4uF5lWhY43xgzhlgnfyDUITSNSnXw4kR04DiZP3++N3r0aAs25NiSxcAiGRMGZs+e7Z8uIIQQQgghrozSy8U1wT/iq1SpYtdJIv/yyy/Nitq/f38TN8eOHbN/5COos2fPbqnI9G8T5sRtwtJIVyZAjUA1qnBYfhEE/MNfiJjgnMMqzix4l0oeG5jpzUgszlHOwVCGFHKC5ejnJi/BuUlwnfDbZKwfLSE1atSwkWO0gbCggdPk+PHj3g8//OA988wz1gPObHMWze666y47hj169FDWghBCCCHEFZDoFtdEw4YNbR4y8A910skZDcY/7rNmzWqhath3sbLS442wJmSN/lJ6tElZDgQBQFWOKhuhbELExm1B9ZWsAIL5COqLCULUGKlF4BoCMhwC+qhav/DCCzYKzLV1xAQOFqz5zDVngcKBm+WNN96wKQONGjXyZsyYYa0hQgghhBDickL/X5oi3sCuSnAaUPVCvLjRYARXcR993ghtbL8O/vFOhTxYcNM3ivWc5GSSy4WIDSTbMw+ev1S8XZhfVJw7d84SvOlTBhLzCQsLBxgfxgLYI488Ym6S2Py+aQHhb+/evSM9RtUclwoLbsz/JmxNCCGEEEJEjUS3uGqwnbqwNOZn33333ZHSkbH6Uv3u3Lmzt3LlSrsfYUQP7YMPPmhBVo6jR49akBpiAKurqmbi34DoJgkfgYhDgpnuQ4cONVFNRsAnn3zidenSxc5RBCTnK+ciDBs2zM65UAerOOGFOAOYNEBfNkFzUVnRae2oWLGit3nzZpvlHV3YXP369a3iPX78eO/rr7++Dp9CCCGEECLpIdEtrhqqho7gsUuI7e3bt1v4EuOb9u/fb0Kb/u9169Z5Y8eO9dq3b29WdJKUqW5jTadaHht7sBCBIpEgNXcdEY1gJCysQoUKNg8eezQik75kzkXGZSEWHe75oQ45CvTA09PdtGlTawEhXG7KlCnWAsKUAH7L/FY5TixWTJo0yX7DgcdrzJgx1jqCs6Vt27a2X8b6CSGEEEKIy1ETnrhqAkcIuXFhwaPBmjRpYhVsqoqI9JYtW9qFqtvvv//ulSlTxqra9IYjxNOlS6dvRPwrVq1aZaO/3Dm5ZMkSq2jjnpg+fbq1MyAkZ82aZYs7gbkCiEnSuVns4b5wWPBhpB/OE6YLUKFmNJj7/dLbXqdOHUt+Z8HswIEDZsfnNxw4p5twRDIZEOy0kiDWBwwY4L3++uv6DQshhBBCBKFKt7hqEDZOeJNIfubMmShHg1EN4x/pzEsm6GrixImWeuxmBGNR79mzp/6xLq4KRKNjwoQJ5qSgCrtmzRqb8U7AGkn62M2DcwXcKDF3Xzhx7733WsWaRYdLly55xYsXN9cJIYY4BFKkSGEiPHPmzJc9l7wGxLaDsW0XLlyQxVwIIYQQIgpU6RZXzX/+8x+rWvMP9/Pnz9vsY9KOv/vuuyhHgwHWX3pv2Y5/6EOpUqX8I4yE+Lfs3bvX/iIC6TF25ybnIYFfXOfcJLk7MFeA0XbuvsD9hCO4TRjXR+XfZTJgwQ9clLgSOFuA37UQQgghhIiMRLe4JrCQI7oBgU2PNiFWgI03GAKrsKlWqlTJfx99tkJcLbQpAC0LiEcnGLFFU8V1uERzqt7p06f3du/e7U8xD9xPuJIyZUqrViO+qXjT4+3cKDHB84DFN5wrCkIUQgghhPj/yF4urgnm/TJWCE6fPu1VrlzZ7LzRcezYMQtp2rJli93OmTOn9XMLcbUgtp1oxkXhBCNVW8bQ0ZtM7zFinATzadOmWWgf5y7Ba8H7CVdwpPDbpRWEELq8efPG+rmknEOrVq2s6s1xZlFDCCGEEEJ4XjIf82OEuAYQNuXLl/e+/fZb/32klFMFJzka2y9BV5MnT7Z+UWcrT5Mmjff5559bb6kQV0ujRo2sdxs432hhoGUBB8bAgQO9P//808uQIYNVuvmLjRyxTRAYOQM4NOC5556zILBwZcGCBWbPJ6ehSJEidh+/4W3btplDheNXo0YNb+TIkTaijTTzPXv2WGsIFn7cBcHz0WvWrGm98s5+LoQQQggRjkh0izgBG2+tWrW8DRs2xGp7wpmWL1/uFS5cWN+AuCbIDkAMAnboEiVKeDt27DDLc9myZS3gD9FHhZsRWWxLvzdz5hHkbt2R59x3331h+21gC6fazfFhgSy2sKDGHO/777/fjvPq1av9dnPgMUaPZcmSJZ7euRBCCCFE4kaiW8QZ/EOb9Gjm9X7//fdRboPll7m+2E+pOgpxrVC1RtgxVxoQjghIFnUKFSoU7fPYhkouAhOr+a5du8L+yxg1apRNFpg5c6bXrFmzGI8H/d/MRMfWz/FjDBvXWeCgp965CKiG42oJdwu/EEIIIcITiW4R51A5pLKFJZUKOOKG+dv84xz7KqOIhIhLSMfv2LGjXUfY4bgguTw25yo94HPnzrWZ1JkyZQrrL4bj0bp1a5tvjtX+2WefjTZMDZs+4wA5buvXr7/MJUAPPfO93WJI3759ze4vhBBCCBFuSHQLIUKi2s2s6IULF5qFnGo3c+QRj4sWLbI+5JtvvtkqsNicmeHdu3dvS9nu1auXPZewtX79+nnhDn3ZjFKj6k0bSPv27a1vHpcKbpatW7eamwVbP8cSR0F0tnxs/AUKFLB9ZsyY0YQ4/d9CCCGEEOGERLcQIiTAzkw1lR7jbNmymZguXry4VWzXrl3rff311ybCaYEoU6aM9+GHH1oVl0A1krqXLl1qVdvYjskKdb755htv3LhxZjU/d+5cpMeYcU4AIuFpnTp1smNJ4rlzG5Bcjlh348M+++wzu58ANgS8EEIIIUQ4oZFhQoiQgHaGOnXqmOAGKqpUWrGZc71cuXLe9u3b7THEIOn59CBzvV69ejbODmEu/hdCDsePH+8dPXrUK1mypP+wzJgxw8azYR1ftWqVLXZEREREOmy4CKZMmWLVcHq8HYsXL9bhFUIIIUTYIdEthAgJyA/IkSOHXcfGTBWWUXa33Xabfxs30opcgUOHDnk//vijjbpjTJbbh4gMx8YdN8b/uYA1FjJuuOEGs6AHwwIG4XYce+z+DhesJoQQQggRTiRP6DcghBBxCYnahKO98847Jha57UA0wujRo71WrVp5adOmtWRt+pVF9Fy6dMl//JIlS+Zf1OjTp0+0PfYOHAXB+xFCCCGECCckuoUQIQHCmX7uxo0bW38xfdqIPEZZXbx40du0aZN/hBi93syTPnXqlPfMM8/YX7cPcTlMHwCOI5b9du3a2aJGdP3vVMAdzEN3sMghhBBCCBFuSHQLIUICxtGRQI6ledCgQXZhDnfXrl29ypUreylTprT0cqAPeeXKlV6qVKm8sWPH2m1GjZUoUSKhP0aihH54wuigVq1aFrDGokZ0MHqNBZC77rrL27Nnj/9+7P5CCCGEEOGG0suFECHB6dOnbYQVVe6ePXvG+nm//PKLiUTGYXXr1s0bMWJEvL7PpAh28pw5c9ocb+zlFSpUsL8samzbts1C7LDy16hRw8az/fe///Weeuopcxrs3LnTO3v2rPXRHzlyxLvjjjsS+uMIIYQQQlxXJLqFECFD27Ztvblz53pfffWVpZbHBCKS/u/Zs2f7+5CxnZPMLSLz2GOPeUuWLLHrrmc+0EYeDIK7YcOGNicdWrRo4XcaCCGEEEKEE0ovF0KEDC+88IL3xx9/mJ2cOdNXglFhzJKeNWuWCUIHdnNxOf379zeLvhsb1qBBA7OQRwV99DVr1vQLbqz7jBETQgghhAhHVOkWQoQMAwcONHs5FViq2ASjMW966dKlNiMa6zPhX4hyqtv79+/3smfPbr3HjA7DBs1zDxw4EGnUlfhfFixYYNVrN0IMqlev7tWuXdtGizGTe/78+d6aNWv8jzNajOP/4IMP6jAKIYQQIiyR6BZChAz58uWzdG1X9UYA/vDDD1769Om98+fPe9myZTOhTZWblPPDhw97kyZNslCwXLly2bYQERFhzxeX8+GHH3qNGjXyfv/99xgPD8cdoV6xYkUdSiGEEEKELbKXCyFCBoK64N577zXhvHfvXq9JkyZ2m8o1VVmSyosWLWq28rJly1qQGiKcSq3j6NGjCfgpEjeEpWEr5zjmyJEjym3y5MljrgKSyyW4hRBCCBHuaGSYECJkoJoNN998s/2lkk31GoG4cOFCE4KAtRzq1atnF5K1K1WqZEncQF+4iB5s+4xnww3AKDHcA1jz6d2+5557bMTYlULWhBBCCCHCCYluIUTIQLX6zJkz3o8//uj9+uuv/pRtepB/++03/3Y33nij/WVEGKKRcVgIRUeaNGkS5P0nNTiOJL0r7V0IIYQQInpUihBChAyEpjmb+cMPP2yhavRrY3cmUfvixYveF1984RUqVMi2Y9Z02rRprSp78uTJy/YjhBBCCCHEtaIgNSFEyIBtvGnTpv7U7DJlytj1Dh062N/Ro0fb2CvmRdPj/dFHH3l9+/a1xzZt2mSzutOlS2c93W48lhBCCCGEENeCRLcQImT4888/LaGcqjVV7Hnz5lnP9pXAes42H3zwgd3u3r27N2zYsOv0joUQQgghRKgje7kQImQgEK1Hjx52nTndjAWbPHlypLnSgZw6dSqS4L7tttu8jh07Xtf3LIQQQgghQhtVuoUQIQViu2XLlt6MGTP892XPnt17+umnLfCLZPMTJ07Y43PmzLHqOCRPntxmUD/wwAMJ+O6FEEIIIUSoIdEthAg5mLvdqVMnb/z48bHanlFXCxYskOAWQgghhBBxjuzlQoiQg6r1W2+95S1fvtyrXr16tNuRXP788897O3bskOAWQgghhBDxgirdQoiQZ9++fd77779vqeTnz5+3OdxFihTxGjRo4KVKlSqh354QQgghhAhhJLqFEEIIIYQQQoh4QvZyIYQQQgghhBAinpDoFkIIIYQQQggh4gmJbiGEEEIIIYQQIp6Q6BZCCCGEEEIIIeIJiW4hhBBCCCGEECKekOgWQgghhBBCCCHiCYluIYQQQgghhBAinpDoFkIIIYQQQggh4gmJbiGEEEIIIYQQIp6Q6BZCCCGEEEIIIeIJiW4hhBBCCCGEECKekOgWQgghhBBCCCHiCYluIYQQQgghhBAinpDoFkIIIYQQQggh4gmJbiGEEEIIIYQQIp6Q6BZCCCGEEEIIIeIJiW4hhBBCCCGEECKekOgWQgghhBBCCCHiCYluIYQQQgghhBAinpDoFkIIIYQQQggh4gmJbiGEEEIIIYQQIp6Q6BZCCCGEEEIIIbz44f8BJMvjgVNbpkkAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_network(network.graph)" - ] - }, - { - "cell_type": "markdown", - "id": "5d3030f5", - "metadata": {}, - "source": [ - "Notice that in the visualisation above, some graph nodes have a thicker outline — these are the *nodes* (junctions and boundaries), while the others are *break points* along each reach.\n", - "\n", - "#### Mapping original IDs to integer IDs\n", - "\n", - "Internally, the `Network` relabels all nodes and break points with consecutive integers for efficient indexing. The original IDs used by the simulation tool are preserved and can be looked up with `find()`:\n", - "\n", - "In this Res1D example the original node IDs are strings. `find(node=...)` returns the corresponding integer ID:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "d9d23a8b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "252" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "network.find(node=\"98\")" - ] - }, - { - "cell_type": "markdown", - "id": "ae495c5d", - "metadata": {}, - "source": [ - "Break points are identified in the original network by the edge (reach) they belong to and their distance from the start node.\n", - "\n", - "> **Note:** The current `Network` implementation assumes a directed edge, so distance is always measured from the start node." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "30c88717", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "131" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "network.find(reach=\"44l1\", distance=44.841)" - ] - }, - { - "cell_type": "markdown", - "id": "a7e41a31", - "metadata": {}, - "source": [ - "Multiple IDs can be looked up in a single call. For break points, each distance value corresponds to the edge at the same position in the `edge` list (one-to-one pairing):" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "e25a4ba8", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "([241, 3, 40], [131, 133])" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "network.find(node=[\"92\", \"101\", \"113\"]), network.find(reach=[\"44l1\", \"45l1\"], distance=[44.841, 37.206])" - ] - }, + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "fdb0d0b9", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:39.674362Z", + "iopub.status.busy": "2026-10-01T14:06:39.673720Z", + "iopub.status.idle": "2026-10-01T14:06:41.426728Z", + "shell.execute_reply": "2026-10-01T14:06:41.425939Z" + } + }, + "outputs": [], + "source": [ + "import modelskill as ms\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from mikeio1d.network import Network" + ] + }, + { + "cell_type": "markdown", + "id": "b643e568", + "metadata": {}, + "source": [ + "# 1D network workflow\n", + "\n", + "This notebook shows how to use `modelskill` to evaluate model results from 1D network simulations, such as collection systems or river networks. The workflow follows the same four-step pattern used elsewhere in `modelskill`: define model results → define observations → match → compare.\n", + "\n", + "## Loading network results\n", + "\n", + "The `Network` class organises data from a network simulation (e.g. a sewer system or a river) into a form that `modelskill` can work with. It knows which time series every node and break point carries without reading any of them, reads only the ones asked for, and exposes the topology via a `networkx` graph.\n", + "\n", + "It comes from [mikeio1d](https://github.com/DHI/mikeio1d), which reads the result files, and it arrives with `modelskill[network]`.\n", + "\n", + "### Loading from a supported format\n", + "\n", + "`Network.open` reads MIKE 1D (`.res1d`), MIKE 11 (`.res11`) and EPANET (`.res`) results:\n", + "\n", + "```python\n", + "from mikeio1d.network import Network\n", + "\n", + "network = Network.open(\"path/to/results.res1d\")\n", + "```\n", + "\n", + "It also takes the EPANET companion files. See mikeio1d's documentation for those, and for building a network from a format it does not read.\n", + "\n", + "### Break points\n", + "\n", + "A reach can carry **break points** — intermediate locations along it (e.g. cross-section chainages) with their own time-series data. They are locations you can compare against, addressed by their reach and their position along it.\n", + "\n", + "### Example" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cd363bae", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:41.429782Z", + "iopub.status.busy": "2026-10-01T14:06:41.428903Z", + "iopub.status.idle": "2026-10-01T14:06:41.626013Z", + "shell.execute_reply": "2026-10-01T14:06:41.624750Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "c4f36cfd", - "metadata": {}, - "source": [ - "Use `recall()` to translate integer IDs back to the original identifiers. This is useful when you want to know which original node or break point corresponds to a given integer ID:" + "data": { + "text/plain": [ + "\n", + "Reaches: 118\n", + "Nodes: 495\n", + "Quantities: ['WaterLevel', 'Discharge']\n", + "Time: 1994-08-07 16:35:00 - 1994-08-07 18:35:00" ] - }, + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network = Network.open(\"../tests/testdata/network.res1d\")\n", + "network" + ] + }, + { + "cell_type": "markdown", + "id": "33a451d3", + "metadata": {}, + "source": [ + "All time-series data stored in the network can be read into an `xarray.Dataset` with `to_dataset()`. It reads every series in the file, so on a large model read only the locations you need with `read()` instead. The dataset has one variable per physical quantity, and its `node_id`, `reach` and `position` coordinates carry the names the model gave each location, so a column can be read without holding on to the network:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2a2d7414", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:41.627674Z", + "iopub.status.busy": "2026-10-01T14:06:41.627530Z", + "iopub.status.idle": "2026-10-01T14:06:41.901921Z", + "shell.execute_reply": "2026-10-01T14:06:41.900093Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 11, - "id": "cb1ae550", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "({'node': '98'},\n", - " {'reach': '45l1', 'distance': 37.20599457458005},\n", - " [{'node': '98'}, {'reach': '45l1', 'distance': 37.20599457458005}])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset> Size: 498kB\n",
+       "Dimensions:     (time: 110, graph_node: 495)\n",
+       "Coordinates:\n",
+       "  * time        (time) datetime64[ns] 880B 1994-08-07T16:35:00 ... 1994-08-07...\n",
+       "  * graph_node  (graph_node) int64 4kB 0 1 2 3 4 5 6 ... 489 490 491 492 493 494\n",
+       "    node_id     (graph_node) <U17 34kB '100' '99' '' '' '' ... '' '' '' '' ''\n",
+       "    reach       (graph_node) <U10 20kB '' '' ... 'Pump:115p1' 'Pump:115p1'\n",
+       "    position    (graph_node) float64 4kB nan nan 0.0 23.84 ... 0.0 41.21 82.43\n",
+       "Data variables:\n",
+       "    WaterLevel  (time, graph_node) float32 218kB 195.4 194.7 195.4 ... nan 195.0\n",
+       "    Discharge   (time, graph_node) float32 218kB nan nan nan ... nan 0.0 nan
" ], - "source": [ - "network.recall(252), network.recall(133), network.recall([252, 133]) " + "text/plain": [ + " Size: 498kB\n", + "Dimensions: (time: 110, graph_node: 495)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 880B 1994-08-07T16:35:00 ... 1994-08-07...\n", + " * graph_node (graph_node) int64 4kB 0 1 2 3 4 5 6 ... 489 490 491 492 493 494\n", + " node_id (graph_node) " ] - }, + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network.to_networkx()" + ] + }, + { + "cell_type": "markdown", + "id": "885523e1", + "metadata": {}, + "source": [ + "Querying the underlying `networkx` graph lets you run topology-based analyses (e.g. shortest path, connected components) directly on the network. The visualisation below plots the graph, highlighting boundary/junction nodes with a thicker outline:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "1d068e44", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:41.916566Z", + "iopub.status.busy": "2026-10-01T14:06:41.916333Z", + "iopub.status.idle": "2026-10-01T14:06:41.922357Z", + "shell.execute_reply": "2026-10-01T14:06:41.921328Z" + } + }, + "outputs": [], + "source": [ + "def network_layout(network, graph):\n", + " \"\"\"Place every graph node: named nodes from the reach skeleton, break points\n", + " interpolated along the reach that carries them.\n", + "\n", + " The graph joins a named node to the break point at the same chainage with a\n", + " zero-length edge, so a layout that reads edge lengths as distances cannot be\n", + " run over the graph itself. The skeleton of named nodes carries the reach\n", + " lengths without those edges, and every break point has a chainage that says\n", + " where along its reach it belongs.\n", + " \"\"\"\n", + " skeleton = nx.Graph()\n", + " for reach in network.reaches.values():\n", + " skeleton.add_edge(reach.start, reach.end, length=reach.length or 1.0)\n", + " named = nx.kamada_kawai_layout(skeleton, weight=\"length\")\n", + "\n", + " graph_id = {a: n for n, a in nx.get_node_attributes(graph, \"address\").items()}\n", + " pos = {graph_id[name]: np.asarray(p) for name, p in named.items()}\n", + " for reach in network.reaches.values():\n", + " start, end = np.asarray(named[reach.start]), np.asarray(named[reach.end])\n", + " span = reach.end_position - reach.start_position\n", + " for bp in reach.breakpoints:\n", + " if span == 0:\n", + " fraction = 0.0\n", + " else:\n", + " fraction = (bp[1] - reach.start_position) / span\n", + " pos[graph_id[bp]] = start + fraction * (end - start)\n", + " return pos\n", + "\n", + "\n", + "def plot_network(network, ax=None):\n", + " \"\"\"Draw the network: named nodes as large circles, break points as small ones.\"\"\"\n", + " graph = network.to_networkx()\n", + " pos = network_layout(network, graph)\n", + "\n", + " # Each graph node's address says what it stands for: a named node, or a break\n", + " # point given as a (reach, position) pair.\n", + " nodes = [n for n in graph if isinstance(graph.nodes[n][\"address\"], str)]\n", + " breakpoints = [n for n in graph if not isinstance(graph.nodes[n][\"address\"], str)]\n", + "\n", + " if ax is None:\n", + " _, ax = plt.subplots(figsize=(10, 9), layout=\"tight\")\n", + " nx.draw_networkx_edges(graph, pos, ax=ax, edge_color=\"0.55\", width=0.8)\n", + " # break points first, so a break point coincident with a node hides beneath it\n", + " nx.draw_networkx_nodes(graph, pos, ax=ax, nodelist=breakpoints, node_size=14,\n", + " node_color=\"white\", edgecolors=\"0.45\", linewidths=0.6)\n", + " nx.draw_networkx_nodes(graph, pos, ax=ax, nodelist=nodes, node_size=55,\n", + " node_color=\"white\", edgecolors=\"black\", linewidths=1.4)\n", + " ax.set_aspect(\"equal\")\n", + " ax.set_axis_off()\n", + " return ax" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "53ab2b9c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:41.923681Z", + "iopub.status.busy": "2026-10-01T14:06:41.923543Z", + "iopub.status.idle": "2026-10-01T14:06:43.130014Z", + "shell.execute_reply": "2026-10-01T14:06:43.128914Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 13, - "id": "edec2e5a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - ": WaterLevel" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "network_model = ms.NetworkModelResult(network, item=\"WaterLevel\")\n", - "network_model" + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAyoAAAN5CAYAAADn08+eAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAt1tJREFUeJzs3XtYnPWd//8XEGKOQxIPJBJ1SALJTAiO2IPQ0KPbeKoNKqZRVxZh43572O5uk81eV9htt0uubWu2293ttjYljlg1iLbQbavGtmIlkKiRTIBkkiHESRQN8YBMzmGG+/eHv5lmOA+nexiej+vKVXvPPTNvYELu9/35vN/vOMMwDAEAAABAFIk3OwAAAAAA6IlEBQAAAEDUIVEBAAAAEHVIVAAAAABEHRIVAAAAAFGHRAUAAABA1CFRAQAAABB1SFQAAAAARB0SFQAAAABRh0QFAAAAQNQhUQEAAAAQdUhUAAAAAEQdEhUAAAAAUYdEBQAAAEDUIVEBAAAAEHVIVAAAAABEHRIVAAAAAFGHRAUAAABA1CFRAQAAABB1SFQAAAAARB0SFQAAAABRh0QFAAAAQNQhUQEAAAAQdUhUAAAAAEQdEhUAAAAAUYdEBQAAAEDUIVEBAAAAEHVIVAAAAABEHRIVAAAAAFGHRAUAAABA1CFRAQAAABB1SFQAAAAARB0SFQAAAABRh0QFAAAAQNQhUQEAAAAQdUhUAAAAAEQdEhUAAAAAUYdEBQAAAEDUIVEBAAAAEHVIVAAAAABEHRIVAAAAAFGHRAUAAABA1CFRAQAAABB1SFQAAAAARB0SFQAAAABRh0QFAAAAQNQhUQEAAAAQdUhUAAAAAEQdEhUAAAAAUYdEBQAAAEDUIVEBAAAAEHVIVAAAAABEHRIVAAAAAFGHRAUAAABA1CFRAQAAABB1SFQAAAAARB0SFQAAAABRh0QFAAAAQNQhUQEAAAAQdUhUAAAAAEQdEhUAAAAAUYdEBQAAAEDUIVEBAAAAEHVIVAAAAABEHRIVAAAAAFGHRAUAAABA1CFRAQAAABB1SFQAAAAARB0SFQAAAABRh0QFAAAAQNQhUQEAAAAQdUhUAAAAAEQdEhUAAAAAUYdEBQAAAEDUIVEBAAAAEHVIVAAAAABEHRIVAAAAAFFnitkBYGCGYai+vl7V1dXq6OjQ3LlztXr1auXk5CguLs7s8AAAAIAxEWcYhmF2EOhbY2OjCgoK5HK5ej3mcDhUXl6uzMzM8Q8MAAAAGGMkKlGqsbFRubm58vl8Sk1NVVFRkZYsWaLDhw+rrKxMXq9XFotFtbW1JCsAAACIOSQqUcgwDGVlZcnlcqmwsFBbt27VlCl/3qXn9/u1bt06OZ1OORwONTQ0sA0MAAAAMYVEJQrV1dVp5cqVSk1NlcfjCUtSgvx+v9LS0uT1elVXV6ecnBwTIgUAAADGBl2/olB1dbUkqaioqM8kRZKmTJmi4uJiSVJVVdV4hQYAAACMCxKVKNTR0SFJWrJkyYDnLV68OOx8AAAAIFaQqEShuXPnSpIOHz484Hmtra1h5wMAAACxgkQlCq1evVqSVFZWJr/f3+c5fr9fZWVlkqS8vLzxCg0AAAAYFyQqUSgnJ0cOh0Ner1fr1q3rlawEu355vV45HA5lZ2ebFCkAAAAwNuj6FaUunqNitVpVXFysxYsXq7W1lTkqAAAAiHkkKlFsoMn0KSkpevTRR3XjjTeOf2AAAADAGCNRiXKGYWjXrl2qqqpSR0eH5s6dq7y8PMXHx8vj8ejee+9VQkKC2WECAAAAo4pEZYIKBAL6xS9+oeXLl+vjH/+42eEAAAAAo4pi+gkqISFBN954o3bt2qXOzk6zwwEAAABGFSsqE9xzzz2ntrY2LVy4UAsXLpTNZmMrGAAAACY8VlQmsEAgoDfffFPTpk3T/Pnz5Xa75XQ6FQgEzA4NAAAAGBESlQnM7XZr7ty5uu++++RwOJSfny+LxSK32212aAAAAMCIkKhMYG1tbUpLSws7lp6erra2NpMiAgAAAEYHicoElpKSopaWlrBjBw8eVEpKikkRAQAAAKODYvoJLBAIyOl0ymKxKD09XW63W2+88YY2bNigxMREs8MDAAAAho1EZYILBAJyu91qa2vT/PnztWfPHjkcDl1//fVmhwYAAAAMG4lKjHnzzTdVVVWlv/qrv5LFYjE7HAAAAGBYqFGJMVdddZXS09P14osvmh0KAAAAMGwkKjHo05/+tNra2noV2gMAAAATBYlKDJoxY4Y+85nP6MUXX9SFCxfMDgcAAACIGDUqMcowDD311FMyDEPz5s1TSkqKbDabEhISzA4NAAAAGBQrKjGqu7tbH374oQKBgJKTk+V2u+V0OhUIBMwODQAAABgUiUqMcrvduvTSS3XffffJ4XAoPz9fFotFbrfb7NAAAACAQZGoxKi2tjalpaWFHUtPT1dbW5tJEQEAAABDR6ISo1JSUnp1/Tp06JBSUlJMiggAAAAYOorpY1QgEJDT6ZTFYlF6eroOHDigt956S9/61rcoqAcAAEDUY0UlRiUkJKiwsFB2u13t7e1aunSpZsyYIa/Xa3ZoAAAAwKCmmB0Axk5CQoIyMjKUkZEhSUpMTNQf//hHXX311UpMTDQ5OgAAAKB/rKhMIitWrJDFYlF9fb3ZoQAAAAADYkVlEomLi9ONN96oJ554QjabTVdccYXZISFChmGovr5e1dXV6ujo0Ny5c7V69Wrl5OQoLi7O7PAAAABGDcX0k9DOnTt17NgxfeUrX1F8PItqE0VjY6MKCgrkcrl6PeZwOFReXq7MzMzxDwwAAGAMkKhMQl1dXXr00Uc1f/58TZ06VSkpKbLZbHQDi2KNjY3Kzc2Vz+dTamqqioqKtGTJEh0+fFhlZWXyer2yWCyqra0lWQEAADGBRGUSCgQC+ulPf6o5c+YoIyNDLS0t6uzsVGFhIclKFDIMQ1lZWXK5XCosLNTWrVs1Zcqfd236/X6tW7dOTqdTDodDDQ0NbAMDAAATHonKJNTc3Cy32638/PzQscrKStnt9lCHMESPuro6rVy5UqmpqfJ4PGFJSpDf71daWpq8Xq/q6uqUk5NjQqQAAACjhwKFSaitrU1paWlhx9LT09XW1mZSRBhIdXW1JKmoqKjPJEWSpkyZouLiYklSVVXVeIUGAAAwZkhUJqGUlBS1tLSEHfN4PEpJSTEpIgyko6NDkrRkyZIBz1u8eHHY+QAAABMZicokZLPZ1NnZqcrKSrlcLj3xxBN66623ZLPZzA4NfZg7d64k6fDhwwOe19raGnY+AADAREaiMgklJCSosLBQdrtd7e3tslqtio+P5058lFq9erUkqaysTH6/v89z/H6/ysrKJEl5eXnjFRoAAMCYoZgekqSXX35ZbW1t+spXvkLHqChD1y8AADAZkajEoOFMLw/OVvnEJz6hU6dOMfk8ylw8R8Vqtaq4uFiLFy9Wa2src1QAAEBMIlGJMSOZXv7ss8/qwQcf1FtvvRXxczH2mEwPAAAmExKVGDKS6eVMPp8YDMPQj3/8Y+3cuVOzZ8/W3LlzlZeXp+zsbFa8AABATCFRiREjqWOgBmJi+d3vfqfLLrtMn/zkJ80OBQAAYMyQqMSIkUwvDz53wYIFWrt2rTo7O3vVpjD5PHo8/fTTWrZsmVasWGF2KAAAAGOG9sQxItLp5T//+c+1Y8cONTc362c/+5kk6Z133tEPf/hDbdu2TVu2bNHKlSuVlZWlxsZGJp9HkTNnzmjmzJlmhwEAADCm+r6ixYQT6fTyY8eOKTk5Wc8//7yefPJJSeqzNsXlcik3N1e1tbVMPo8Sp0+f1owZM8wOAwAAYEyRqMSISKeXZ2Vl6dprr1VhYaECgUCftSkbN24M1aYUFBTozjvvDHsvjL/u7m6dPXuWRAUAAMQ8alRiRLDOxGq1qqWlZUg1KoZhRFTXMn/+fB0/fpwaFROdPn1aDz/8sL75zW/2u8UPAAAgFlCjEiNycnLkcDjk9Xq1bt06+f3+sMeDnbu8Xq+sVquys7Mjrms5fvy4HA6HsrOzx/RrQf/OnDmjSy65hCQFAADEPK52YkRcXJzKy8uVm5srp9OpmpqafqeXFxQU6Omnnw5tExtqXUtiYqLKy8tpTWySQCCgpqYmnTt3Ts3NzbLZbEpISDA7LAAAgDFBohJDMjMzVVtbG5peXlJSEvZ4cHr58uXL5Xa7Qxe5Q61rueeeexj2aJJAICCn06lZs2bpxhtvlNvt1u7du1VYWEiyAgAAYhI1KjHIMAzt2rVLVVVV6ujo6Hd6+XDqWqhNMUdzc7Pcbrfy8/NDxyorK2W325WRkWFiZAAAAGODRGUSi2Qi/bJly9TU1ERthEl27Nih5ORkORyO0DGXy6X29natWrXKvMAAAADGCInKJNfY2Kjc3Fz5fD5ZrdY+61pmzZqlb3/725ozZw5bjUzCigpilWEYqq+vV3V1dWgFePXq1crJyaEeDgAmORIVqLGxMVTX0lOwriUzM5MLYxMFa1SmT5+u5cuXy+PxyOfzkThiQhvq7x4AwOREogJJvetaPvzwQ912220qKCgI3dVkq5G5AoGAfvrTn+qyyy5TRkYGXb8woV28mpuamqqioiItWbJEhw8fDutSWFtbS7ICAJMUiQr6dPFWo+DWjO9///tKTEzUokWL2Jphkp/97Gf60pe+pCuvvNLsUIBhi6Q+zuFwqKGhgd81ADAJkaigT8GtRh0dHdq2bZsOHTrU6xy2ZowvwzD0ox/9SIWFhZozZ47Z4QDDFuw4mJqaKo/HQ8dBAECfmEyPPiUkJOj666/Xd7/7XR06dEipqakqLS1VRUWFSktLZbVa5XK5lJubq8bGRrPDnRTOnTun7u5uzZgxw+xQgBGprq6WJBUVFfXbSXDKlCkqLi6WJFVVVY1XaACAKEKvWfTJMAw98MADOnXqVJ9bMzZu3BjamlFQUMDWjHFw5swZTZkyRVOnTjU7FGBEOjo6JElLliwZ8LzFixeHnQ8AmFxYUUGf6uvr5XK5lJqa2itJkT6627l169bQysquXbtMinTyOHPmjGbOnGl2GMCIzZ07V5J0+PDhAc9rbW0NOx8AMLmQqKBPbM2IPqdPn2bbF2LC6tWrJUllZWXy+/19nuP3+1VWViZJysvLG6/QAABRhEQFfWJrRvQ5c+YMiQpiQk5OjhwOh7xer9atW9crWQl2/fJ6vXI4HMrOzjYpUgCAmahRQZ/YmhF9SFQQK+Li4vTII4/o05/+tJxOp2pqalRcXKzFixertbU1bI5KeXk59W8AMEnRnhh9CrYPtVqtamlpoX2oSYIzbKqrq9XU1KR58+bpa1/7GjNsMKEF25+///77cjqdtD8HAPSJRAV9YiCb+RobG1VQUCCXy9XrMS7iMJH1HCi7a9cufe973wsNlM3Ly1N2dja/UwBgkiNRQb8aGxuVm5srn88nq9Xa79aM2tpaLphH2cXf+9TUVBUVFWnJkiU6fPgw33tMeDt27FBycrIcDkfomMvlUnt7u1atWmVeYACAqEKiggENdFc/JSVFTz/9NIWuo4zVLMSai7cwdnR0KBAI6Oqrr9Z3vvOd0Ge3srJSdrtdGRkZJkcLAIgWJCoYVHBrRlVVlTo6OjR37lzl5eXp3Xff1ezZs/X5z3/e7BBjSrA+KDU1VR6Ph/ogTGgD3eywWq36/ve/L0ny+XwqLCxUQkLCOEcIAIhWdP3CoOLi4pSTk9PrYvjdd9/Vk08+qY9//OOaPXu2SdHFnkhm2JSUlKiqqopEBVFpKFsYCwsL9dhjj5GkAAB6YY4Khu3yyy/XokWL9Oqrr5odSkxhhg1igWEYKigoCK2UeDwebdq0SWvWrNGmTZvU0tKiwsJCnTlzRqWlpYqP558jAEA4/mXAiNxwww1qbm7WyZMnzQ4lZjDDBrGgvr5eLpdLqampveqspI9WBbdu3Sqr1SqXy6Vdu3aZFCkAIFqRqGBEgqsqr7zyitmhxIzVq1dLksrKynpN7A7y+/0qKyuTJOXl5Y1XaMCQRbKFUZKqqqrGKzQAwARBooIRy87O1v79++Xz+cwOJSbk5OTI4XDI6/Vq3bp1vZKVYNcvr9crh8NB1zVEJbYwAgBGimJ6jNhll10WqlW58cYbzQ5nwouLi1N5eblyc3PldDpVU1PT7wyb8vJyWhMjKrGFEQAwUrQnxqh4//339Ytf/EI5OTnq6OhQSkqKbDYbXXxGgMn0mMiCbbatVqtaWlposw0AiBhbvzAq5syZo66uLh0+fFjJyclyu91yOp0KBAJmhzZhZWZmqqGhQTt27NBnP/tZPfDAA1q/fr3q6urU0NBAkoKoZRiGDMPQ5ZdfLq/Xq+uuu05/+tOfdPF9MbYwAgAGw4oKRkVzc7P279+vNWvWhI4xaXrkAoGAXnrpJe3du1c33XQTq1SIegOtBC5YsEDf/OY3Q80gglsYa2trSbwBAL2QqGBU7NixQ8nJyXI4HKFjLpdL7e3tWrVqlXmBTWCBQEBOp1MzZ86UzWZTS0uLOjs7GYyHqDWUAY8XYwsjAGAgFNNjVKSkpMjtdoclKh6PR3a73bygJji3262kpCTl5+dL+uiirrKyUm63m1UqRJ2eAx57zk7ZuHGj1q1bJ6fTqXnz5un//u//lJOTQzMIAEC/qFHBqLDZbOrs7FRlZaVcLpeefPJJ+Xw+2Ww2s0ObsNra2pSWlhZ2LD09XW1tbSZFBPQvkgGPH3zwgeLi4khSAAADIlHBqEhISFBhYaHsdruOHj2qo0eP6s4772SL0gikpKSopaUl7JjH41FKSopJEQH9Y8AjAGC0sfULoyYhIUEZGRnKyMhQYmKiXn31VepTRsBms6m+vl6/+MUvtGLFCnk8HlapELUY8AgAGG2sqGBM3HDDDXK73frwww/NDmXCSkhI0C233KL29na1t7fLbrdTSI+oxYBHAMBoI1HBmJg3b56WLl2qV155xexQJrQPP/xQV111lVatWqWMjAySFESt1atXS5LKysrk9/v7PCfYlliS8vLyxis0AMAERaKCMcOqyvAYhqG6ujpt2LBB//RP/6RnnnlGdXV1opM4ollOTo4cDoe8Xq/WrVvXK1lhwCMAIFLMUcGYeu655xQXF6ebbrrJ7FAmhIGG5TFzAtHKMAzV19frBz/4gZ599ln5/X5ZrVYVFxdr8eLFam1tZcAjACBiJCoYUx0dHSovL1dBQQF70gcxlGF5XOQh2gyUXPdEsg0AiASJCsbc888/L8MwZLFYVF1drY6ODs2dO1erV69m4Nv/zzAMZWVlyeVy9TksL7htxul0yuFwqKGhge8bTNdfct3S0qL//d//1fHjx5WQkKBbb71VGzduVHZ2Np9bAMCQkahgzO3cuVNf+cpX+hxUyB3Wj9TV1WnlypVKTU2Vx+Ppcw6F3+9XWlqavF6v6urqlJOTY0KkwEdIrgEAY41ieoypxsZG3XrrrWpra1NqaqpKS0tVUVGh0tJSWa1WuVwu5ebmqrGx0exQTcWwPEw0kUyid7lc2rVrl0mRAgAmKhIVjBnDMFRQUCCfz6fCwkJ5PB5t2rRJa9as0aZNm9TS0qLCwkL5fD4VFBRM6q5WDMvDRENyDQAYayQqGDPccR06huVhoiG5BgCMNRIVjBnuuA4dw/Iw0ZBcAwDGGokKxgx3XIeOYXmYaEiuAQBjjUQFY4Y7rkMXFxen8vJyWSwWOZ1OpaWlafPmzaqoqNDmzZuVlpYmp9Mpi8Wi8vJyuifBdCTXAICxRntijJlgy12r1aqWlhZa7g4Bk+kxkVw8R4VJ9ACA0UaigjHDnIXhuXDhgtavXy/DMHT27FnNnTtXeXl5DMtDVPrZz36mH/zgBzpy5Eivx0iuAQAj0XeFMzAKgtuZcnNz5XQ6VVNT0+8dV7Yz/dnx48e1fPlyrVu3ju8JolpnZ6fOnj2rV155RR6PR1VVVero6CC5BgCMClZUMObYzhSZ+vp6dXR06NZbbzU7FGBAL7zwgrq6uvisAgDGBCsqGHOZmZlqaGjQrl27VFVVJa/Xq1OnTumf//mfuePah7feektLly41OwxgQJ2dnTpw4IDuv/9+s0MBAMQoEhWMi7i4OOXk5CgnJ0c+n09lZWX6+Mc/TpLSg9/v1zvvvKMvfOELZocC9CkQCMjtdmvnzp2aO3eukpKSzA4JABCjaE+McTd79mxNnz5d7e3tZocSVQKBgHbu3Klz587p7bffViAQMDskIEwgEJDT6dT+/ft1ww03aNq0aXI6nXxWAQBjgkQF4y4uLk7z588nUblI8ALwnXfe0apVq3Tw4EEuABF13G63kpKStGbNGjkcDq1Zs0YWi0Vut9vs0AAAMYhEBaaYP3++jh8/bnYYUSN4Abh27Vo5HA7l5+dzAYio09bWprS0tLBj6enpamtrMykiAEAsI1GBKVhRCccFICaClJQUHThwIOyYx+NRSkqKSREBAGIZiQpMkZycrI6ODp07d87sUKJCSkqKDh06FHaMC0BEmwULFujw4cN66qmn5HK5VFlZKZ/PJ5vNZnZoAIAYRNcvmGLGjBmyWCxqb2/XNddcY3Y4prPZbPrDH/6gJ554QsuXL5fH4+ECEFHn1Vdf1cc//nFdddVVamtrk91ul81mU0JCgtmhAQBiEIkKTBOsUyFRkRISEnT11VcrPj5e7e3tXAAi6nzwwQc6dOiQ/uqv/kpz5sxRRkaG2SEBAGIciQpMk5ycrHfeecfsMKJCd3e33n77beXn5ys5OdnscIBedu/eLZvNpjlz5pgdCgBgkiBRgWnmz5+vvXv3mh1GVHj33XdlGIYuv/xys0MBZBiG6uvrVV1drY6ODk2bNk3x8fH67ne/a3ZoAIBJhEQFpklOTtapU6d0+vRpzZw50+xwTNXW1qaUlBTFx9PfAuZqbGxUQUGBXC5Xr8dqa2tVXl6uzMzM8Q8MADDpkKjANJdcconmzZun48ePa/HixWaHY6o333xTCxcuNDsMTHKNjY3Kzc2Vz+dTamqqioqKtGTJEh0+fFhlZWVyuVzKzc1VbW0tyQoAYMzFGYZhmB3EZNdzm8XcuXO1evVq5eTkKC4uzuzwxkwgENATTzwhv9+vT3ziE5OqePzin/kHH3ygo0eP6hvf+IZuv/32mP6ZI3oZhqGsrCy5XC4VFhZq69atmjLlz/ey/H6/1q1bJ6fTKYfDoYaGBj6rAIAxRaJisoG2WTgcjpjdZhEIBOR0OjVz5kzZbDa1tLSos7NThYWFMZ+sTNafOaJbXV2dVq5cqdTUVHk8nrAkJcjv9ystLU1er1d1dXXKyckxIVIAwGTBhngTBbdZuFwupaamqrS0VBUVFSotLZXVag1ts2hsbDQ71FHndruVlJSktWvXyuFwKD8/XxaLRW632+zQxtRk/pkjulVXV0uSioqK+kxSJGnKlCkqLi6WJFVVVY1XaACASYoaFZMYhqGCggL5fL4+t1ls3LgxtM2ioKAg5rZZtLW1KS0tLexYenq62traYnY+w2T/mSO6dXR0SJKWLFky4HnBerLg+QAAjBVWVExSX18fuqve84JV+ujO5datW0N32Xft2tXvaxmGobq6Om3YsEHFxcXasGGD6urqFM27+lJSUtTS0hJ2zOPxKCUlxaSIxt5o/syB0TZ37lxJ0uHDhwc8r7W1Nex8AADGComKSSLdZvHLX/5Szc3N2rFjh5qbmxUIBCR9tJUoKytLK1eu1JYtW7Rt2zZt2bJFK1euVFZWVtRuIbLZbOrs7FRlZaVcLpeefPJJ+Xw+2Ww2s0MbM2ytQTRbvXq1JKmsrEx+v7/Pc/x+v8rKyiRJeXl54xUaAGCSIlExSaTbLF599VW53W4lJyfL7XbL6XRq7969E7beISEhQYWFhbLb7WpubtbJkydjvpCerTWIZjk5OXI4HPJ6vVq3bl2vZCXY9cvr9crhcCg7O9ukSAEAkwU1KiaJdJtFUlKS8vPzJX3UGeqpp57SPffcM6HrHRISEpSRkaErrrhCTz31VMwPO2RrDaJZXFycysvLlZOTI6fTqZqaGhUXF2vx4sVqbW1VWVmZvF6vLBaLysvLo+73CQAg9sT2lWEUi3SbxV133RX22NmzZ3Xw4MGYqHe49NJL1d3drQ8++MDsUMYUW2sQ7ebPn6+vfe1rWrFihbxer0pKSrR27VqVlJSEVlIY9ggAGC8kKiaJZJvFsmXLNGPGjLDHn3nmGUmxUe+QkJCgyy+/XMePHzc7lDHF1hpEo0AgoObmZj3//PP61a9+pZtvvln79u1TXV2d1q9fr6KiIq1fv151dXVqaGggSQEAjBu2fpkkuM0iNzd30G0WTz75pF5//XVVVlYqPT1dHo9HnZ2dkmKn3mH+/Pk6fvy4li9fbnYoYyaSnzlbazAegoNXk5KSlJaWpvfee08ej0e5ubnKyclhoCMAwFQkKibKzMxUbW1taEp5SUlJ2OMXTynPzMyU2+1WW1ub7Ha7PvGJT2jnzp1Drnd4//33tW/fPl1zzTWaM2eOpI8uUoKvmZKSIpvNNmAxu2EYqq+vV3V1tTo6OjR37lytXr1aOTk5I76onj9/fp+T2mNN8Gd+55136vDhwwP+zIGxFhy8enH9W2Vlpdxud8zOMwIATBxxRjQP25gkDMPQrl27VFVVFUoA8vLylJ2d3W8CUFdXp5UrV8pqtaqlpaXP7V9+v19paWnyer362c9+pqSkJL399tuaPXu2Fi5cqJaWFiUnJys9PV0tLS3q7Ozst/NWY2NjKKHqaTQurj/44AM99thj+sY3vhHTnb8kqaurSz/72c+UkpKi+vr6If/MgdG2Y8cOJScny+FwhI65XC61t7dr1apV5gUGAIBIVCYswzCUlZUll8vVZ9evYL2D0+mUw+EIdf3q6urSW2+9pVdeeUWBQED33ntv6DkVFRWy2Wy69tprw96rsbFRubm58vl8Sk1NVVFRkZYsWaLDhw+HbVcaSZGtYRj68Y9/rLvvvlvJycnD+6ZMEPv379drr72mgoICkhKYqrm5Wfv379eaNWtCxyorK2W321lRAQCYjkRlArs4gbBarf3WO/SVQPR3J/XZZ59Venq6rr76al1zzTW6/PLLdf3110ecEA3H008/raVLl8b8tqcnnnhCdrtd1113ndmhYJILBAL6j//4Dy1cuFB2u10ejyfU8jzWVzYBANGPRGWCG+6WrObmZrnd7tDedOmjO6lXXXWVLrnkEh07dkxvvvmmvF6vfvjDHyo1NVUej2fQLWZ1dXXDLsB9+eWXde7cOX3xi18c1vMnguPHj6uyslIPPvigLrnkErPDwSR37NgxVVdXKycnR++///6QatUAABgvFNNPcJmZmWpoaIi4xsVms2n37t1hncR8Pp8+8YlPKCEhQVlZWQoEAvra174maWhtkEtKSlRVVTXsRGX+/PnavXv3sJ47Uezbt092u50kBeOuZzOMOXPmaNq0abr99tv1sY99zOzwAADohUQlBsTFxUXcSjQhIUGFhYVhncR63klNSEgIzfoYjzbI8+fP13vvvaeuri4lJiYO+3WiTbC7mtfrVVNTkx544AGzQ8IkM9DK6+9+9zs6zQEAohKJyiSWkJCgjIyMAYtm586dK0lDboN86tQpHT16VAsXLlRCQkJELZBnzZqltrY2feMb35Df7x/V9sdm6TmnorOzU7/5zW+oAcC4GawZhsvlUm5uLhPnAQBRhxoVDCjSNsg//OEPNX36dHV1denqq6/W0aNHNX/+/EFbII91+2Oz9FcLRFcljIfhdgcEACAakKhgQMO50JGkEydOqLa2VufPn+/VAnn58uVasWJF6Fh/d3xbWlr0v//7vzp+/LgSExN1zz336K//+q8n1AoLcypgpuCNhvFohgEAwGiLNzsARLe4uDiVl5fLYrHI6XQqLS1NmzdvVkVFhTZv3qy0tDQ5nU5ZLBaVl5crLi5OcXFxSk5O1uzZs7V8+fKw11u2bJmeffZZPf/88/J4PDp37pwKCgpCLVE9Ho82bdokm82mX/7ylzp+/Likj4YklpeXa+XKlcrKylJjY6MZ346IpaSkqKWlJeyYx+NRSkqKSRFhMqmurpY0tGYYklRVVTVeoQEAMChqVDCozMxM1dbWhrZmlZSUhD3e39aslJQUud3usNUEj8ejT37yk4qLi1NdXZ1+/OMfy+VyKTU1NbRaE0t76m02m3bu3Klf/OIXWrFiRai7ms1mMzs0TALB5hbj0QwDAIDRRqKCIRlOG+T+WiDfeeedSkhI0Gc+8xnt3btX0p/v+BqGEbbC0nOr2caNG0NbzQoKCqJ+T31CQoLsdrtaWlrU3t7eZ3c1YKxE2gwjeD4AANGAGhWMqcG6fhUXF2vbtm2qqKjQmjVrYm5Pvd/v189//nPdfPPNslqtZoeDSSbSZhjR/vcJADC5UKOCMRVsgbxq1SplZGT0Wknoecc31vbUt7S06JJLLtE111xjdiiYhHJycuRwOOT1erVu3brQXKSgYDMMr9crh8Oh7OxskyIFAKA3EhWYavXq1ZKksrIy+f3+iPfUNzU1qaGhQW+99ZbOnz8/prEOx759+3TttddG9fY0xK7hNMMAACBaUKMC0wQCAV24cEEpKSmhO76R7qmfN2+ejh49qtdee02nTp3SnDlzdMUVV4T9mTlzZp/vPdRBlMP17rvvqr29PZSMAWYINsO4/fbb5fV6h9wMAwAAs1GjgnFzcXIwb948tbS0qKurS4sWLdJtt90mn8+n+fPn6/jx48PaU3/mzBmdOHFC7e3tOnHihE6cOKEPP/xQM2fODEtcLrvsMv3qV78KTYsfaBBlJAzDUH19vaqrq9XR0SGfz6cbbrhBf//3f8+dapiqs7NT27Zt03XXXadnn312SM0wAAAwG4kKxkUgEJDT6QwlB83NzTp+/Li+/vWva9q0aX1Oph+NSdrnz5/Xu+++G0pcTpw4obfeektXX3217rvvvtB5Tz31lGw227DvKvcVfxB3rGG21157TceOHdOdd95pdigAAAwZiQrGRXNzsw4cOKC77747dKyyslJ2u10ZGRmSPlqR2LVrl7Zu3art27frwoULslqtKi4u1uLFi9Xa2qqysjJ5vV5ZLJZhz1F57rnntGDBgl7T4p999lldc801vVZfEhMTB3y9wea+jDReYKSeeOIJZWZmasWKFWaHAgDAkJGoYFz89re/1cKFC3slB+3t7Vq1alWv88dyhaK5uVlut1v5+fmhY5WVlbrqqqs0Z86csNWX8+fPa968eb3qXqZNmybpo+QqKytLLpdrVFaAgNEUCAT0+uuv6/e//71uuukmORwOZvgAACYMEhWMubfeeksVFRW68sordc8994SO91xR6Sm4wjLUAZNDFdyGZrFYwgZR9qxRMQxDJ0+eDEtcTpw4oZMnT8piseiKK67Q8ePH9Td/8zcxM/cFsSP4OZ85c6ZsNtuo1WIBADBe6PqFMWMYhl577TXt2rVLn//859XQ0NBrSr3NZuv3+XFxccrJyRn1C/uEhAQVFhaGCvv7mxYfFxcni8Uii8US1i45WLR/4sQJlZeXSxra3JeSkhJVVVWRqGBcuN1uJSUlhVYOHQ6HKisr5Xa7+705AABANCFRwajo2e43NTVVL7zwgt577z2tWbNG8+fP17XXXjtocjBegoMoh3PBNmPGDFmtVlmtVs2bN0/S0Oe+BOfEAGOtra1NaWlpYcfS09PV1tZGogIAmBBIVDBiPTt67d+/X7/+9a9ls9n0l3/5l6F6jpEkB9Eq0rkvwfOBsZaSkqKmpqawujCPxyO73W5eUAAARIDJ9Bixi7eYOBwOrVmzRosWLVJaWlooSYlVwWGOZWVl8vv9fZ7j9/tVVlYmScrLyxuv0DDJ2Ww2eb1ebd++XS6XS5WVlYNutwQAIJqQqGDE+tpiYrPZ9Pbbb5sU0fjJycmRw+GQ1+vVunXreiUrwa5fXq9XDodD2dnZJkWKyebMmTO65JJLlJ6ervb2dtntdgrpAQATClu/MGKzZs3S/v37J+UWk7i4OJWXlys3N1dOp1M1NTV9zn2ZPXu2ysvLaU2McdPS0qKrrrpK119/vdmhAAAwLCQqGJHm5mbt2bNH3d3dEXX0iiWZmZmqra0NzX0pKSkJe3zZsmX68pe/rPnz55sUISYjj8ejZcuWmR0GAADDxhwVDEtXV5defPFFtba26pZbbtFVV10V1vXLzI5eZhlo7surr74ql8ule+65R7NnzzY7VMQgwzBUX1+v6upqnThxQseOHdOmTZv0hS98oddK3sXnBj+rq1evVk5ODqt+AICoQaKCIbm4/bDFYtGhQ4d0ySWX6LbbbuPCewgMw9ALL7yg9vZ2feUrX9HUqVPNDgkxpLGxMbSi15PD4VB5ebkyMzMjPhcAADORqGBQPdsPNzc3q729Xd/4xje44I5AIBBQVVWV4uLilJeXp/h4ellg5BobG5Wbmyufz6fU1FQVFRVpyZIlOnz4cKhGymKxqLa2VpKGfC7JCgDAbCQqGFRzc7MOHDigu+++O3SssrJSdrs9pmaijIfz58+roqJCV155pW688Ua22WBEDMNQVlaWXC6XCgsLtXXrVk2Z8ufSw2DXOafTqUWLFikxMVGHDh0a9FyHw6GGhgY+nwAAU5GoYFD/93//p6uvvjqsq5fL5VJ7e7tWrVplXmATlM/n05NPPqnrr79eH//4x80OBxNYXV2dVq5cqdTUVHk8nrDEI8jv9ystLU1er1eShnxuXV2dcnJyxvpLAACgX+w9meQMw1BdXZ02bNig4uJibdiwQXV1dQrmr0eOHNHBgwd14MCBsOd5PB6lpKSYEfKEZ7FYlJeXp927d8vtdqu5uVk7duxQc3OzAoGA2eFhAqmurpYkFRUV9Zl4SNKUKVNUXFwc+v9DPbeqqmp0gwUAIEK0J57E+iuq3bJlixwOh775zW/K5/Pp1ltv1a5duyZt++GxkJycrJtvvlnbt29Xenq6li5dKrfbrd27dzOUD0PW0dEhSVqyZMmA5y1evDj030M9N/jaAIDhocPiyJGoTFKDFeC6XC599atf1fPPP6/ly5dr2bJloa5fdrt9UrYfHm3nzp3TkiVLtGbNGkkfdVyqrKyU2+2m9gdDMnfuXEnS4cOHBzyvtbU19N9DPTf42gCAyA12M5gOi0NDjcokFEkBLkW1Y2fHjh1KTk6m9gfDFqxRsVqtamlpGVKNylDPpUYFAIYnkm6MJCsDo0YlhgxWbxJUX18vl8ul1NTUXkmK9NE+9a1bt8pqtcrlcmnXrl3j+WVMGikpKWppaQk7Ru0PIpGTkyOHwyGv16t169bJ7/eHPR686eD1erVo0SItXbp0SOc6HA5lZ2eP55cCADHBMAwVFBTI5/OpsLBQHo9HmzZt0po1a7Rp0ya1tLSosLBQPp9PBQUFva7REI4VlRgx2BC3Rx55RImJiWpra1NFRYUeffRRlZaWatOmTf2+5ubNm1VSUqL169froYceGsPoJ6fgfBqLxaL09HTt379fb7/9tv7+7/++32JnoKeL79xZrVYVFxdr8eLFam1tHXCOymDncpcPACIXaTdGVq8HxtVQDBhKvcmnPvUpffe739WNN96oo0ePSqKo1mwJCQkqLCwM1f4sW7ZMZ86c0a5du5Sbm2t2eJggMjMz9dJLL+n222+X1+tVSUlJ2OM990LX1taGbmoMdi4AIDKRdGMsKSlRVVUVicoASFQiFEkHh/Ho9tBzibHnVq6NGzeG6k2eeOIJfetb39L111+vmpoaimqjQEJCgjIyMkLF81arVRUVFUpKSuJiEUNmsVhUUlKijIyMsN83eXl5ys7ODvt9k5mZqYaGBu3atUtVVVUDngsAiEyk3Ri5GTwIA0O2b98+w+FwGJJ6/XE4HMa+ffuGdW5f/H6/0dTUZDz//PNGU1OT4ff7+zxv586dhiQjNTXV6Orq6vOcrq4uw2q1GpKMurq60HOsVuuQn4Px8+abbxo/+tGPjDfeeMPsUDBB/N///Z/x8ssvmx0GAEwqPa/VOjs7jTVr1hiSjNLS0gGfW1paakgy1q9fP07RTkwU0w9RcHtVsAi9tLRUFRUVKi0tDRWd5+bmqrGxMaJz+xKsXXC73UpOTpbb7ZbT6QwbBhgIBPThhx/qsccekxTZELecnBxZrVaKaqPUwoUL9cUvflG/+c1v9O6775odDqLc+fPndeTIEdntdrNDAYCYYQzSoKjntVpTU5N+9KMfhXZDlJWV9bq+CvL7/SorK5Mk5eXljc8XNEFRTD8ERgTtfJcuXaquri4dOXJk2K1/m5ubdeDAAd19992hY9u3b1dXV5emTp0qn8+nU6dOKS4uTr/61a+0c+dOVVRUhOZx9KWiokJr167V6tWrtXbtWh04cED/+Z//SVFtFNu9e7f27dune+65R7NmzWJoFPrU1NSkffv26b777jM7FACICYM1KCovL1d8fHyva7WKigotX75c999/PyMgRgmJyhBE2sFB0pDP/eUvf6n09HR1dnbK5/Ops7NTx44d0xe/+MVe8zX27t2rT37yk7JYLLJYLJo1a5Y2btyoLVu2DLmD15133qnvfOc7stls2r9//6B/EUlSzGMYhnbs2KE9e/bomWee0b59+3qdw88JTz31lNLS0pSVlWV2KAAw4Q11Bsrf/d3fKS8vr89ZaAsWLKDD4mgxb9fZxLF+/fqI9htGcu4Xv/hF46mnnjKee+45o76+3mhubjZqamqMioqKsPOfeuopo6mpqdfrjLTepLu726irqzPWr19vFBUVGevXrzfq6uqM7u7uAePH+GhoaDBmzJgRqkMqLS01KioqjNLS0tDP1GKxDFrzhNjj9/uNV155xSgtLTX27NnTbx0bAGBouru7Q/XFhYWFva6rurq6jMLCQkOScdVVVxnbt28Pe/zia7WR1irjI3T9GoJIOzhEcu5VV10Vtmwo/XnfY2VlpdLT0+XxeOTz+WSz2Xq9TnDgm8vl0rp16/pdYuyv3iQuLk45OTm0xotChmHogQce0JkzZwbt6FZQUMDy8SQS/B0xc+ZM3XrrrWppadHevXtVWFiohIQEs8MDgAlpqAOxa2pq5PV65XK5FB8f3+e1Gh0WRweJyhAE2/MOtZ1vJOf21fq353wNu90um83W5wVIXFycysvLlZubK6fTqZqamn6XGMvLy/mLMYFE8gvT5XJp165dJJyThNvtlsViCd3kcDgcqqyslNvtDrW6BgBEJtIZKF1dXbLb7f1eq3EzeOTo+jUEq1evljT0Dg6RnNtft4fgfI1Vq1YpIyNjwLukmZmZqq2tlcPhCA18W7t2rUpKSkIrKeyDnHgi+YUpfdTRDZNDW1ub0tPTw46lp6erra3NpIgAYOKLdAdNZ2fnkK/VMDwkKkMQ3F41lHa+S5cu1aJFi8a99W9wibGurk7r169XUVGR1q9fr7q6OjU0NJCkTEAMjUJ/UlJS5Ha7w455PB6lpKSYFBEATCyBQEDNzc3asWOHmpubFQgENH36dEkj2xWD0cXWryGIZHtVZWWlJJmyFYslxtgS6ZZDfmFOHunp6aqurlZFRYWWLVs2YB0bAEw2hmEM2NI/WOeXlJSktLQ07d+/X88991zomqysrEwbN27st3MrM1DGD+2JIzCUvtrBlYtIzgX6EmyLbbVa1dLSMmir67q6OpLUSaK1tVV/+MMflJOTo7ffflspKSn91rEBwGQy1Bkobrdb+fn5oceefPJJLVq0SP/v//0/ZqBEERKVCBmGMeQODpGcC/RkRDBo9Nprr9XevXv5XE0Sv/nNbzRnzhzl5uaaHQoARI2hzkD5zne+o8997nPMQJkASFSAKHbxL93+fmFOnz5d3/nOd/S3f/u3mjZtmtkhY4ydP39eDz/8sO677z5deumlZocDAFEhkpt7CxYs0EMPPaR777039HhlZaXsdrsyMjLYFRNFSFSAKDfYL8yysjIdO3ZMnZ2duuOOO2SxWMY/SIybpqYm7du3T/fdd5/ZoQCAKfqqQVm0aJG++tWvKjU1VR6PZ9Dt0hs3blRWVlbYDJSLZ1GxKyY6UEwPRLmhDI267rrr9Mc//lHbt2/X6tWr9e6776qtrY3ahRh04MAB2e12s8MAAFMMdPNOkm699VZmoMQQVlSAGBG8+/PHP/5R6enpWrp0qVpaWtTZ2cnE8hjh8/m0bds2Pfjgg5oxY4bZ4QCIEoN1uYoVQ6lBmT59unbv3t3v1qyKigqtXbtWRUVFYfPvEJ1YUQFiRFxcnCwWi9LS0rRmzRpJTCyf6HpefJw7d052uz3U6x8A+lth2LJlS0zVUxiGoYKCgtAWrZ41KBs3bgzVoBQUFPTbkYuW/hMLKypADNmxY4eSk5P77GSyatUq8wJDxCjmBDCYoXa5ioUOVcGW/UOtQemrZT8t/SceJtMDMSQlJUUtLS1hxw4cOKAFCxaYFBGGI3jx4XK5lJqaqtLSUlVUVKi0tFRWq1Uul0u5ublqbGw0O1QAJum5wuDxeLRp0yatWbNGmzZtUktLiwoLC+Xz+VRQUKCJfl+6urpaklRUVDRoDYok/fKXvwx7LNj1y+v1yuFwKDs7e0zjxehg6xcQQ2w2m3bv3q3Kykqlp6fr0KFDeuONNzRr1izZ7fZ+f7kjeozW9gYAsa2+vj50M6Pn7wnpo4v2rVu3qqamRi6XS7t27ZrQKwgdHR2SpCVLlgx43uLFiyVJW7du1bx58/qcgVJeXs7vzQmCFRUghiQkJKiwsFB2u13t7e1avny5/vZv/1anTp3SM888o3PnzpkdIgYx1IuP4MrKrl27TIoUgJkiXWGoqqoar9DGxJw5cyRJhw8fHvC8YA3KqVOnVFJSorVr16qkpCS0khIL2+AmExIVIMYkJCQoIyNDq1atUkZGhmbPnq38/HxNmzZN27dvl8/nMztEDGCyXXwAGJ5IVxiC50e7QCCg5uZm7dixQ83Nzbpw4YKamppCvw/Lysrk9/v7fK7f7w918nr44Ye1fv16FRUVaf369aqrq1NDQwNJygTDPhBgEkhMTNTtt9+uF198Udu3b1deXp6uuOIKs8NCH2L14gPA6Ap2rRrqCsNE6HIVCATkdDqVlJSktLQ0NTU16de//rXmz5+v++67Tzt27JDL5dK6dev6nTwfXDlZt24d27tiAIkKMEnEx8frC1/4gmbPnq2nnnpKt912m06fPs1gyCgTixcfAEbf6tWrtWXLFpWVlWnjxo39dsEKrjDk5eWNd4ghQ53z4na7ZbFYlJ+fL+mjDocVFRWy2+3KyMhQeXm5cnNz5XQ6VVNTo+LiYmpQYhztiYFJqKmpSVVVVUpPT9eyZcsYDBlFgi04rVarWlpahtWCE0DsMwxDWVlZcrlcfTbeCK4wOJ1OORwO0xpvDLXV+vnz57V9+3Y5HI4BW+zTun1yIVEBJqHm5mbt378/NBhSkiorK0N3rWCeffv2KTs7W2fPno3qiw8A5rt4jorVau1zhWH69Omqq6vTddddZ2p8A815+e///m+dPHlSCQkJmjdv3qD/NhmGoV27dqmqqiq0QpOXl6fs7Gx+H8YYEhVgEmIwZHS6+B/1+Ph4dXd393vxEStD3ACMzGArDPfcc49uvvnmcb8JFcmKj9Vq1Z/+9CddeeWVevTRR2WxWJSeni6PxxNq1c5q/+REogJMQs3NzXK73aF9wJL0xBNPaPny5WHJC8ZPz3/Uv/71r6uoqKjPi4/p06dr165duvbaa8c/UABRJ7jC8MMf/lCBQEBLliwJrTDs27fPlJtQw50kHwgE5Ha7qZ+EJIrpgUmpr8GQx44dU1xcnJYuXarp06ebHeKk09f8lIaGhrDtDUlJSXryySd1/PhxnT592uyQAUSJuLg45eTkyGKxyO1266677lJ9fb3+8R//Ua+//rquueYazZo1q1fxeqQGK4q/OMmoqKiQNLRW6yUlJaqqqlJOTk6oxT7bkCGRqACTUnAwZPAflOXLl+vLX/6yduzYoYqKCt15552yWCxmhzmp9DU/JXjxcXGx/Lx588L+UQeAIJvNpmeeeUb/+I//KK/XG/bYo48+OqJi8/62mG3ZskUOh0OPPPKIXn/99VBr4WPHjkmi1TpGhkQFmKT6umv1pS99SS+++KKefPJJ3XHHHcxaGUfBdsP8ow5guPbv36///M//7Ld43eVyKTc3N+L6tsGK4l0ulz796U/r29/+dmgYbVZWll588UVarWNkDAC4SHd3t/HKK68Y//3f/2288cYbht/vN5qamoznn3/eaGpqMvx+v9khxpx9+/YZU6dONSQZpaWlA55bWlpqSDLWr18/TtEBmAi6u7sNh8NhSDIKCwuNrq6usMe7urqMwsJCQ5LhcDiM7u7u0GMD/Z6P5HWXLl0aet2dO3cakgyr1drrORc/12q1GpKMurq60fpWIIZQTA+gTwcOHNALL7yg+Ph4LViwQGlpaVE1b8UY4gCxaGdcVEQvifkpAIZlJMXrF0+D7/l7frivaxiGFi1aJK/XS6t1DFu82QEAiE52u12ZmZmaN2+e8vPz5XA4lJ+fHyrWNFNjY6OysrK0cuVKbdmyRdu2bdOWLVu0cuVKZWVlqbGx0dT4IhEsordarbr22mvl9Xq1bt06+f3+sPOC/6h7vV45HA5lZ2ebFDGAaNRXnVtPweJ1Sdq8ebMef/xx/fSnP9X06dPDfs9Pnz5dDz/8sJ588kn9+7//e0Sv+73vfU8ul0tPP/20CgoKZLFY5HQ6lZaWps2bN6uiokKbN29WWlqanE4nk+QxIGpUAPSrq6urV+eV9PR0tbW1mdaRZSh7pYezB9sswYuL4uJifelLX1Jubq6cTqdqamr6nJ9yySWX8I86gF6CdWtDrXObMWOGPvnJT+r111/XsmXLws5Zvny53G63rr/+elVVVUX0uomJiWpvb5fdbtedd96pO+64I1SEX1JSEvYcJsljMCQqAPqVkpIit9sdNlvF4/HIbrebEo9hGCooKAgNAOu5lWDjxo2hrQQFBQUTYivBxRcXmZmZqq2t7fcfdUm65ZZb+EcdQC9JSUmSNOTidavVqrS0NJ0/f77P3/MrVqxQenq6rFZrRK+7aNGisJktmZmZvVqtM0keQ0WiAqBfPeet7N+/X8eOHdMtt9xiSjx9zRq52JQpU7R161bV1NTI5XJp165dUV/HEex0E7wI6O8fdZ/Pp61bt4buWgKAJHV3d6u5uVnx8R/t5i8rK9PGjRv7rSUpKyuTJOXl5Unq/Xs+OA3eZrNJklavXq0tW7ZE/LoX66vVOjAUFNMDGNDFA7ySk5N19OhRvf/++7rzzjuVlJQ0rkXtGzZs0JYtW1RaWqpNmzb1e97mzZtVUlKi9evX66GHHhrVGEZbsFCVInoAA+k5sX3ZsmU6cuSIdu7cKcMwlJOTo3vuuUculyvi4vWBpsFf3PCDoniMNxIVABHp7u7WSy+9pEOHDmnZsmX6h3/4h14DwKSx2XtcXFysbdu2qaKiQmvWrOn3vIqKCq1du1ZFRUWhu3zRiosAAIPp2Znr0KFDOnLkiObOnaucnBwtX75cCQkJYTV8Vqu1zzo3i8Uyojkqo/m6wGDY+gUgIvHx8frc5z6n48eP65ZbbtG5c+fGrai95zap/kykAWJxcXEqLy8ftIiezjjA5OV2u5WUlKT8/HxJH90I2r59u2w2W9jv18Hq3IZ7A2msXhcYDCsqACJm1ipALG+TamxsVF5eno4cOdLrMS4CgMmturpaVqs1rODd5XKpvb09rHA9yDCMMSleH6vXBfpDogIgYsMdADZSkSRIVqtVzc3Nmjlz5ojfdzycP39eP/3pT5WWlqaXXnqJiwAA8vl8qq+v1969e2W1WrV27drQY5WVlbLb7aa1igfGA1u/AEQsksFiJSUlqqqqGpVEJZJtUv/wD/+gxx9/XLfddptSUlJG/N5j7dChQ7rssst066236tZbbzU7HAAmOnPmjF555RU1NjYqLS1NDz74oH71q1/125kLiFUkKgAiFulgseD5o2Goe6VXrFghl8ulZ555Rp/61KfkcDh08ODBPrvaRIOmpiatWLHC7DAAmOj8+fN6/fXXtWfPHl111VW65557dPnll0uSCgsLQ5257HZ71P0OA8YCiQqAiJld1D7UAWLXXXedkpOT9Zvf/EYvvviirFar0tPT5Xa7tXv3bhUWFkbFP/QnTpzQe++912s6NIDY1LMdcFpampqbm7V7925deumluuOOO7Rw4cKw5yQkJCgjI4OtXphUqFEBELGJVtT++uuv6+DBg7r33ntDx6Jpf/eLL76oCxcu6KabbjI7FABjrGer4YMHD6q1tVULFy5Ubm6uUlNTqUkD/n/xZgcAYOLJycmRw+GQ1+vVunXr5Pf7wx4PFrV7vV5dddVVuvrqq02K9CPvvfeeli9fHnYsPT1dbW1tJkX0Z36/X263OyoSJgBjz+12y2KxKD8/Xw6HQ1/5yle0aNEiZWVladGiRSQpwEVIVABELFjUbrFY5HQ6lZaWps2bN6uiokKbN29WWlqanE6nLBaLfvCDH+iXv/yldu3ape7ublPiTUlJUUtLS9ixAwcOKDk52ZR4LtbS0qLp06dPiIJ/ACN34MABpaenhx2z2Wx6++23TYoIiF7UqAAYlkgGgLW3t+t3v/udjh49qltuuUUWi2VcY7XZbNq9e3eoY86hQ4d09OhRdXV1yWq1as6cOaFzu7u79bOf/UyPPPKIfD6fLBaLHnjgAT344IOKjx/9ezvNzc1asWIFd1GBGHf8+HHV1tbqjTfeUCAQCJuJ4vF4ZLfbzQsOiFLUqAAYkaEOAOvq6lJNTY08Ho+++MUv9rqjONZ6Fq+mp6errq5O+/fv10033aQlS5bomWee0f3336+zZ8/2ev706dP12GOP6a677hq1mD788EM5nU49+OCDmjFjxqi9LoDo8f7776uurk5vvPGGrrvuOmVlZamiokIWiyWs1XC0NPcAogmJCoBx5fF49MILLyg9PV2f+9znlJiYaGo8hw4d0gsvvKAPPvhA//qv/yrDMJSamqqioiItWbJEhw8fDs1niYuLU2Vl5aglKzt37tQHH3yg22+/fVReD0D08Pl82rVrV6gG7YYbbtCsWbMk9b5xQqthoG8kKgDGnc/n03PPPafTp0/r1ltvNb1W5L333tOVV16prq6uQSfeT58+XadOnRrxNrDu7m5t3bpVq1atUmpq6ki/BAAmujjxuOyyy9TZ2ammpiYtWbJEOTk5o96iHZgsSFQAmKK7u1uvvPKKXn31Va1cuVJZWVmm1Wn89Kc/1Ve/+lWlpqbK4/EM2m754Ycf1oMPPjis9wpe0DQ3N6utrU1/+7d/a/qqEoDhC7YbDm7lCv7dLigo0Pz5880OD5jQ6PoFwBTx8fHKzs7WXXfdpYaGBv3qV7/S6dOnTYnlkUcekSQVFRX1maRI0pQpU1RcXCxJKisrG9b7BC9o3G63li1bpiuvvFLl5eUKBALDCxxRyzAM1dXVacOGDSouLtaGDRtUV1cn7g3GnubmZs2cOVN33323HA6H7rvvPqWmpuq9994zOzRgwqPrFwBTpaSk6P7779fvf/97PfbYY7rpppvGfSuUz+eTJC1ZsmTA8xYvXhx2fqTcbreSkpKUn58v6aPOaJWVlcxRiTGNjY2hbngX27JlS1g3PEQfwzBUX1+v6urqUHOQ1atXKycnp9eKb3d3t9xut1544QX9xV/8RdhjwTlN/L0GRoZEBYDpLrnkEt166606cOCAfvOb32jFihXKzc1VXFzcuBScBtslHz58eMDzWltbw86PVFtbm9LS0sKOcUETWxobG5Wbmyufz9dnUwaXy6Xc3FzV1taSrESZoSaYhmGotbVVO3fuVFdXlzIyMuTxeGg3DIwBalQARJWOjg797ne/UyAQ0NmzZ3XppZcqLS1NLS0t6uzsHJMWnsEaFavVqpaWljGrUWlubtaBAwd09913h45VVlbKbreTqMQAwzCUlZUll8s1aFMGh8OhhoYG5udEicESTK/XK4vFoqefflrvvfeeOjs7dcMNN4SSzYtrVGg3DIweEhUAUScQCOjpp5+W3+/XfffdFzo+Vhf13d3dmjVrls6ePTumXb8CgYAefvhhJSUlhe7CckETO+rq6rRy5cohN2Woq6tTTk6OCZHiYpEkmCkpKXr66ad1/fXXa+rUqaFzaDcMjA2K6QFEnYSEBM2dO7dXQhLcJjXa4uPj9dhjjykuLk5Op1NpaWnavHmzKioqtHnzZqWlpcnpdCouLk6PPfbYsFsTJyQk6JprrtGUKVPU3t4uu91OkhIDgoXzf/d3fydp6E0ZqqqqxitEDKC+vl4ul0upqam9khTpo5/Z1q1bZbVa1dbWJsMwwpIU6aO/2xkZGVq1apUyMjL4Ow2MEhIVAFEpJSVFLS0tYccOHjyolJSUMXm/u+66S5WVlZo+fbq8Xq9KSkq0du1alZSUyOv1KjExUf/+7/8+omGP586dU2trq2688UYuaGJEY2OjsrKytHLlSu3Zs0fS0JsydHR0jHl8GFx1dbUkEkwgGpGoAIhKNptNnZ2dqqyslMvl0vbt2+XxeNTR0aHu7u4xec+77rpLp06d0sMPP6yPfexjSk9P18c+9jE9/PDDeuONNzRt2jQdO3Zs2K/vdrt1+eWX67LLLhvFqGGWYF1D8G78ypUrJQ29KQNDAKNDMGEkwQSiD12/AESlhIQEFRYWhvZ9r1ixQp///Of13HPP6c0339Stt96qpKSkUX/f+Ph4Pfjgg30Wy3/+85/Xb3/7W91zzz166623ItqPbhiGmpqadN111416zBh/hmGooKAgVGO0detWvfLKK1q5cqXKysq0cePGfmtUgnN48vLyxjvsmDeU9sI960lmzpwpiQQTiEYU0wOYULq6uvSnP/1JbrdbN954o2w227i+/44dO9TQ0KDFixcrPT19yN3I2tvb9dRTT+lv/uZveu1vx8TTV+E8Xb/M1V97YUmh9sLLly8P69B14MABvfjii9q2bduQu/7RBAEYP6yoAJhQEhMTdeONN8pqtWrHjh06evSoPv/5z4/bxf/8+fN19dVXh1oMD3VoY1NTk5YtW0aSEiP6qmuIi4tTeXm5cnNz5XQ6VVNTo+LiYi1evFitra1hbW7Ly8tJUkbRUOfX/PznP9esWbPC/v7Gx8errq5OBw8e1Lp16/pNML1erxwOh7Kzs836MoFJhxUVABPWyZMn9dxzz+nkyZO69dZbNX/+/DF/zx07dig5OTlsuJvL5VJ7e7tWrVrV53O6urr08MMP66677tKCBQvGPEaMveLiYm3btk0VFRVas2ZN2GNDubPPsMfRE8lK1oIFC/S73/0ubAumy+XSrl279E//9E/y+XyyWq39JpgM6gTGF8X0ACas2bNn66677lJGRoaeeuopvfrqqxrrey99dSPzeDwDdiPzeDyyWCzjkkhhfATrFPqqa8jMzFRDQ0Noe5gkfexjH1NdXZ0aGhq40B1lkbQXfuedd/Tb3/427HGPx6Pc3FzV1tbK4XD02fXP4XCQpAAmYOsXgAktPj5en/zkJ3X11Vfrd7/7nY4ePaqbb75Zs2bNGpP3s9ls2r17tyorK5Wenq7m5madPn16wFqZpqYmrVixgq0+MeS2227Tli1b+i2cj4uL0yc+8Qm99dZbkqT/+q//oq5hjETSXrikpEQ7d+4M/f0NDl0NNsRoaGjQrl27VFVVFSrGz8vLU3Z2Nn9/AROQqACICQsWLNBf/uVf6sUXX9Rjjz2mVatWhdqJjqae3ci6u7s1e/bsfs//4IMPdPz4cX35y18e9Vhgjg8//FBHjx7VNddcI6/XS13DKIu0c9eRI0ckDb298MKFC2W329XW1ia73R7WtS8uLk45OTkklUCUIFEBEDMuueQS3XzzzXK73Xr22Wdlt9v16U9/WomJiZKGdgE0FMEp1BkZGerq6lJFRYVqamp044039jq3qalJS5Ys0fTp00ft64R5Dh8+rOeff152u11VVVX67Gc/S+H8KOqvvmfLli29OnclJSUpLS1N58+flzT09sLz5s0L/f0FEN0opgcQE3omIdOnT9e8efOUmpqq2267TW+//faYFTj7fD794he/0Kc//WllZGSE4vjggw907Ngx/c3f/I3uuOMOLlYnsO7ubu3cuVP79u3TX/zFX2jZsmWSKJwfTYN17gomftu2bZPf79dXvvIVSX9uFU17YSD2kKgAmPAGulhMS0vTpz71KT3zzDM6derUgBdAIymWPXr0qH7yk5/o2WefVXNzc6/HuWidWC7eWjRv3jy1trbq3Llz+tKXvqRLL7007FzDMKhrGKGRdO4yDEM2m02HDh1ifg0QY0hUAExoQ7kLGx8fr+7u7mFdxPScYt3fFPrGxkbl5OTo9OnTY5YMYXwEAoGwrUXNzc1655139I1vfEPTpk0zO7yY1NcAzZ4uXhX57ne/q3/+538OPfbQQw/pu9/9rk6dOkV7YSCGkKgAmLCGchf2y1/+sp599tkhXwBdvC2k5wVrf1PomUgeW5qbm3XgwIHQUEBJqqyslN1up64hQoPVhQVvBHznO9/RL3/5S5WWlmrTpk39vt7mzZtVUlKiL37xiyoqKgrr3HX99dfrgQceYBseEEMopgcwYQ1lfsKyZcv07LPPDrl16fe+9z0VFhYqMTFRHR0dmjlzpvLz8yV9dLGzfft27dixQ0uWLFFiYqKmTJmivXv3DmmOQ01NTWi4HHvko9fRo0eVnp4ediw9PV1tbW0kKhEYrDD+kUce0euvv66kpCQFAgFJI+/cRXthILaQqAAwxWh04BrK/ITOzk5JQ78ASkxMlNVqVVdXl06cOKEVK1aEnWez2fSnP/1JHR0d6urqkt/v11NPPTVoHBcnQ1VVVSQqUaq9vV0ej0ednZ1yOByh4x6PR3a73bzAJpjBtmS6XC59+tOf1re//W0VFxfr1VdflTTyzl20FwZiC4kKgHE3lBakF2/R6K9OpKOjQ9LASchAE8QvFrwAWrRoUahId8aMGXK73b0uWL/whS+EXSDt3r170DikPydDwbgxfgZLjA3DUFNTk2pqanTDDTdo//79fQ4FxOAMw1BBQYF8Pl+fWyE3btwY2gpZVlamb33rW1q9evWAAzSlj7ZQlpWVSZLy8vLG7esBYB4SFQDjaih3WnNzc0NFrz3rRPbv368//vGPWrx4cWjq90BJyEgugHpOoe/vgjXSZCh4PsbHYIlxWVmZTpw4Ia/Xq7y8PF199dX6xCc+EUqOew4FxMCGsiUzuBXy0KFDoa2QDodDLpeLAZoA/swAgHHS3d1tOBwOQ5JRWFhodHV1hT3e1dVlFBYWGpIMh8NhnD592vj9739vPPnkk2HnPf7440ZlZaVRWVlpSDKsVmuv1wq6cOGCMXXq1CG/Z3d3d9jjfr/faGpqMp5//nmjqanJ8Pv9vd5j586dg8bR1dVlWK1WQ5JRV1c36PcKo2Pfvn2GxWIxJBmpqalGaWmpUVFRYZSWloZ+HtOnTze+973vGSdPnjQ73Jiwfv16Q5JRWlo64HmlpaWGJONLX/qSsXfvXuMHP/iBMX369NDfpdLSUmP79u1hPyuLxWLs27dvnL4SAGYjUQEwboIX9KmpqUO6oP/6179uPPTQQ8bevXvDztm7d6/x/PPPR5T4JCQkjNkFUKQJWM9kCGODn4s5ioqKDElGRUXFgOdt377dkGTccccdoRsBDQ0NoZ9Zzz8Oh4MkBZhk2PoFYNwMpfj94qLzhIQE3XTTTX3WidjtdsXFxam8vFy5ublyOp2qqanpd37Ctm3btHnzZrlcLpWUlIS950hbl0YSR3l5Od2HxkkkW5DoxjY6gg0mpMjqwlatWhU6TucuAEEkKgDGzVCK36U/F52fOnVq0DqRzMxM1dbWhmoQBkpC7rzzzjG7ABosjsWLF+uv//qvtXz58hG9D4Yu0sSYbmyD66+xxbvvvqvGxkYdOHBACxculKRhF8bTuQtAEIkKgHEznKLzhIQEFRYWDljYnJmZOaS7sGN9ATRQHB/72Me0fft21dfXKzc3d0zeH+EiTYzpxjawno0tDhw4oBdffFGXXXaZ3n33XS1dulR33XWXkpOT9bvf/Y7CeAAjRqICYNwMtwNXQkJCnzMTLhYtd2EHiuO2227TE088oauuukpWq3X8g5tk6MY2utxut5KSksIGoD7++OOaOXOm7rjjDk2bNi10LlshAYyGOMMwDLODADA5GIahrKwsuVyuPucrBO+0Op1OORwONTQ0xNxFTHNzs15++WXdf//9mjVrltnhxLSdO3cqNzdXVqtVLS0t/SbGaWlp8nq9qqurMz3RHQ/GMIat+v1+VVZWym63h9WLuVwutbe3h9WYBPXXFloaeV0YgMmBRAXAuLp4jorVau33TmtwjkqsMQxDzz33nE6dOqVly5bpnXfeCdvrj+HpWTtx1VVX6Y9//KO++c1v6q233pq0iXFPkSYPHR0damxs1P79+3XhwgUlJydr7dq1oceDyUt/q52GYVAYD2DYSFQAjLvJfqf17Nmz+uEPf6hFixbJZrOppaVFnZ2dKiwsJFkZhp61EwcPHlRLS4uuu+46XXHFFfqLv/iLSZsYX2ywYavB78VLL72kmTNnat++fWpra9PixYt17bXX6sorr9Sjjz4qi8US1tiCzy2AsUKiAsAUk/lOa3Nzs/bv3681a9aEjg12Zxr9a25ultvtDtVOSFJFRUWormmyJ8ZSZNsuFy5cqH/5l39RZmamMjIyNHPmzNB5/XX9AoCxQKICAONsx44dSk5OHvJe/8lusJqK559/XvPnzx/w+zmZE2NJqqur08qVK5WamiqPxzNovc7OnTv1qU99yoRIAeDP6PoFAOMsJSWl3yGWCNffasiWLVvkcDj0k5/8RG+++abee++9Ab+f0dIVziyRzpSprq4mUQFgOhIVABhnPYdYNjc367333tOdd95pdmhRZbCaCpfLpc9//vPasmWLDMPodygomCkDYGIiUQGAcdZziOXVV1+t9957T+3t7bryyivNDm9UDKcFbs/nFxQUhIq1e9ZUbNy4MVRTUVZWptdee00HDx7sdyhoLBuobuTcuXNqaWnRiRMnJDFTBsDEQo0KAESBPXv2aO/evfrLv/zLsMF5E1Gkxet9XWjv3r07opqKyTIDpaeeHc9aWlr04Ycf6tOf/rQOHTqkI0eO6LLLLtP58+dVXFzMTBkAE0q82QEAAKTrr79el112mXbs2KGJfP8ouF3L5XIpNTVVpaWlqqioUGlpqaxWq1wul3Jzc9XY2CjpzxfabrdbycnJcrvdcjqdevrppyUNraZCkqqqqsbnC4wyF0+Ldzgcys/P1/Tp0/Xss8/q0ksv1f3336/77rtPDzzwgBwOh7xer9atWye/3x/2OsGuX16vVw6HQ9nZ2SZ9RQDwZ2z9AoAoEBcXp1WrVukXv/iFXC6XHA7HiLZOjafgNq+qqio5nc4hbdcqKCjQ66+/rtdff12zZ88OtRZ2OBx6/PHHtXPnTknUVAzEMAx5PB6lpaWFHc/IyFB7e3tYMXxcXJzKy8uVm5srp9OpmpqafmfKlJeXR91nDMDkRKICAFFixowZuvXWW/U///M/uv/++9Xc3Bz2eLDT1aOPPqpTp05FRRLT1zav1NTUXkmK9NEKyNatW1VTUyOXy6V/+Id/0OWXX65bb7017LyMjAxdc801ev3116mp6ENnZ6cOHjwot9utd955R+fOnRtSB7nMzEzV1taGfl4lJSVhj0+WmTIAJg5qVAAgijQ2NionJ0enT5/ud3p4fHy8uru7ez13vJOYnl25UlJStHPnTpWWlmrTpk39Pm/z5s0qKSnR1772Nf31X/+1PB5P2LDGyspKnT9/Xvfff/+krKnoq2bnwoUL8ng8oeTkmmuukc1mU2pqqh5//PGIpsVP9pkyACYOEhUAiBKDTQ9vaGjQDTfcoK6urnFLYvrr3pWdna3rr78+LNa/+Zu/0bZt21RRUaE1a9b0+3VWVFRo7dq1Kioq0s9+9jM5nc5eF9p/9Vd/pY9//ONDmqTucDjU0NAQExfZPYvjDx48qKNHjyoxMVHz58+XzWbT0qVLNWPGjLDnMC0eQEwyAABRYefOnYYkIzU11ejq6gp7rLu723A4HIYko7CwsNfjr7/+upGYmBh6fmlpqVFRUWGUlpYaVqvVkGTEx8cbknr9cTgcxr59+3rFs2/fvtB79vyzcOHCXrGuX7/ekGSUlpYO+HWWlpYakoz169cbhmEYfr/faGpqMp5//nmjqanJ8Pv9ofe3WCyGJMNqtRqlpaXG9u3bw74mi8XSZ+wT1b59+4yKioqwY08++aSxe/dukyICAPOQqABAlBjoQn+sk5ieF/wXJwn9PadnrMEYrVZrrxiCurq6Qs+vq6sb9HsyULLUX4I10XR3dxvvvPOO8eKLLxrf+973jL1794Y9vnfvXuP55583JzgAMBHF9AAQJQaaHl5dXS2p73a99fX1oXbAPbdIGYahoqIidXV1DdqJa82aNfrP//xPpaSkDDps0W63q6WlJSzWnJwcORwOuVwurVu3rt/tWpG0wM3MzFRDQ8OErakYaFtWR0eH3G63Dh48qNOnTystLU0f//jH5fF4hlQcDwCxjkQFAKJEsHNVX52uxiKJkcI7cR08eFDHjx/Xa6+9NuhzvvSlL+mHP/xhWKxj1QI3Li5OOTk5E65Yvme9idvtVn19vbKysuTxeHTixAlZrVZ96lOf0qJFi5SYmBh6TmVlZVjNjs1mM/vLAYBxx8BHAIgSq1evliSVlZX1Gsg3FklM0MWDE/fv3y+fzzfoc+64444+Yw22wA0OFywpKdHatWtVUlISWkmpra2dcC1wDcNQXV2dNmzYoOLiYm3YsEF1dXUDDufsbxjja6+9poyMDD344INavXq1li5dqsTERElSQkKCCgsLZbfb1d7eLrvdPmAHLwCIZSQqABAlglun+poePhZJzMUuHpw4lOfk5OTommuu6TPWzMxMvfrqq7rlllskSfPmzdO3vvUt1dXVqaGhYcIlKY2NjcrKytLKlSu1ZcsWbdu2TVu2bNHKlSuVlZWlxsbGXs8JBALav39/r2GMK1as0KJFi5SZmanp06f3+X4JCQnKyMjQqlWrlJGRQZICYNJi6xcARImBtk4dPnxYU6dODSUGF2/JWr16tbZs2aKysjJt3LgxbBVkoCTmYn0NThzoOXFxcfrSl76kH//4x4Nu86qpqZlwyUlQz1kxPVtCu1wu5ebmqra2VitWrNA777wjt9utQ4cO6ezZs+ru7qbeBACGiTkqABBlGhsbdeedd/aZKCQkJCgQCMhqtYYlMf/2b/+mCxcu9Cp+r6ur08qVK4c8ONHpdOrYsWP69re/PeTnpKWlqaWlpdc5aWlpevrpp3XttdeOwndl/BmDzLW5eJZLWlqaNmzYoPPnzys9PV02m00LFizQo48+GtEwRgDAn5GoAECUMQxDZWVlSkpK0muvvRbW6WrmzJn6q7/6K7lcrl7PizSJkcIvtpctWxbq+nX//fcPedii0+nU3Xff3Wey4nA4VF5ePiYrKsMddDjU5wWTvNTUVHk8nkETtoqKCt15551h5zGMEQCGj0QFAKLM+++/r8cff1xf/epXQ0XWFzMMo892vZEmMT23aF1c5H7xlqeBnrNt2zYVFRX1uzWqr9fuaTgX8z07arW0tKizs3PQ1Yq+nvfhhx/qnnvuUXd3t7q6ukJ//vVf/1VlZWUqLS3Vpk2b+n3NzZs3q6SkROvXr9dDDz00YNwAgKEjUQGAKPPqq6+qra1NeXl5ET93OElMf6sejY2NKigo6PM5U6dO1V//9V/rt7/9rY4ePTqklZeGhoZeLYkjSTgCgYAuXLigrq4u7d+/X8eOHdOaNWtCj2/fvl0zZszQ/PnzQ+f1/HPixAnNnTtX9957b+h5v/jFL3Ts2DFNmzZNU6ZMUWJiohITE/X444/r5ZdfVkVFRdj79FRRUaG1a9eqqKhIZWVl/Z4HAIgMxfQAEGWOHDky7LkZA80ciXRwYl/DFv1+v+rr69XS0qL//d//laQhzWhxuVx65plnlJGREZY4HDt2TDNnzlR+fr6kj5KmJ554Qj/96U81Y8aMsHO7u7tDr33hwgXdfPPNYe9ns9lUV1enxMRETZ06VVOmTNHs2bNDicfUqVPV2NioZcuWhT1vxYoVuvTSS3XTTTcpPv7PzTA9Ho9efvnlYTUiAACMHIkKAESRs2fP6u2339att9466q89nMGJfT0nuGrzzW9+U3v27BnSjJaSkhI5nU795V/+ZShpSExM1IcffqhPfOITYc9Zvny5Dh48qBtuuCHs3IsTkP3798vtdvfqqPWZz3xGGRkZ/X493d3dfT7PbreHJSnSwN3Ugvx+f2gVZTgrYACA/pGoAECUCAQCeumllyRJR48ejdrC62Dycu2112rPnj1DntFy5ZVXau3atWGPNTc395k4ZGRkyGq19vuaNptNu3fvjniCeyTPC861cblcvVpCS3/e2hYcZJmdnT3gewMAIkOiAgBRIFirMWPGDH3+85+X2+3W7t27o7qV7UhmtAQNN+EITnAPFuHb7fYhJXaRPG+guTY9mwqUl5f3uX0OADB8FNMDQBRobm7WgQMHdPfdd4eOVVZWym63D7iVyUyRzmipq6vrc9tZtLfwHaipwFi2XwaAyY5EBQCiwI4dO5ScnBy2Bcrlcqm9vV2rVq0yL7ABRDIQsb+uXxNFf93U+mtEAAAYORIVAIgCzc3NampqCqvhiPYVFWno81YGmqMCAEBfSFQAIAr4/X59//vf1+LFi7Vs2bJQrUY016gEsTUKADAWKKYHgCjg8/k0Y8YMLVu2TO3t7UMuDo8Gfc1bYWsUAGCkWFEBgCjw2muv6c0339Qdd9xhdigAAESF+MFPAQCMtSNHjmjRokVmhwEAQNQgUQEAk509e1ZtbW0kKgAAXIREBQBM5vV6ddlll8lisZgdCgAAUYNEBQBM1traqsWLF5sdBgAAUYVEBQBMFAgE5PV62fYFAEAPtCcGABMEAgG53W653W6dO3dOl19+udkhAQAQVVhRAYBxFggE5HQ65Xa7lZaWpiuvvFKPPvqoAoGA2aEBABA1SFQAYJy53W4lJSUpPz9fDodD99xzjywWi9xut9mhAQAQNUhUAGCctbW1KS0tLexYenq62traTIoIAIDoQ6ICAOMsJSVFLS0tYcc8Ho9SUlJMiggAgOgTZxiGYXYQADCZBGtUpk+fruXLl8vj8cjn86mwsFAJCQlmhwcAQFQgUQEAE5w+fVr/9V//pRUrVig1NVU2m40kBQCAi9CeGABMcOzYMS1cuFBf+tKXzA4FAICoRI0KAJjgyJEjDHkEAGAAJCoAMM66u7uZRg8AwCBIVABgnLW1tSk+Pl4LFiwwOxQAAKIWiQoAjLPW1lalpqYqLi7O7FAAAIhaJCoAMM6OHDmixYsXmx0GAABRjUQFAMZRR0eHfD6frrnmGrNDAQAgqtGeGADGQSAQkNvt1uuvv66ZM2cyMwUAgEGwogIAYyw4id7tduvaa6/VrFmz5HQ6FQgEzA4NAICoRaICAGPM7XYrKSlJ+fn5cjgcWrt2rSwWi9xut9mhAQAQtUhUAGCMtbW1KS0tLexYenq62traTIoIAIDoR6ICAGMsJSVFLS0tYcc8Ho9SUlJMiggAgOgXZxiGYXYQABDLAoGAHnnkEU2fPl0ZGRnyeDzy+XwqLCykqB4AgH6QqADAODh27JieeuoprVixQgsXLpTNZiNJAQBgACQqADAO/vSnP+ns2bO66aabzA4FAIAJgRoVABgHR44c0aJFi8wOAwCACYNEBQDGWEdHhzo7O2W1Ws0OBQCACYNEBQDG2JEjR7Rw4UJNnTrV7FAAAJgwppgdAADEGsMwVF9fr+rqanV0dOjEiRNavXq1DMNQXFyc2eEBADAhUEwPAKOosbFRBQUFcrlcvR5zOBwqLy9XZmbm+AcGAMAEQ6ICAKOksbFRubm58vl8Sk1NVVFRkZYsWaLDhw+rrKxMXq9XFotFtbW1JCsAAAyCRAUARoFhGMrKypLL5VJhYaG2bt2qKVP+vLvW7/dr3bp1cjqdcjgcamhoYBsYAAADIFEBgFFQV1enlStXKjU1VR6PJyxJCfL7/UpLS5PX61VdXZ1ycnJMiBQAgImBrl8AJjXDMFRXV6cNGzaouLhYGzZsUF1dnfq7hxMIBNTc3KwdO3aoublZgUBAklRdXS1JKioq6jNJkaQpU6aouLhYklRVVTX6XwwAADGErl8AJq3+Ct+3bNnSZ+F7IBCQ0+lUUlKS0tLS5Ha7tXv3bq1Zs0ZHjx6VJC1ZsmTA91y8eLGkj2arAACA/pGoAJiUBit8d7lcys3NDSt8d7vdslgsys/Pl/RRF6/HH39c//3f/62TJ09Kkg4fPjzg+7a2tkqS5s6dO4ZfHQAAEx81KgDGTM95InPnztXq1auVk5MTVkg+1PNGM66hFr5fe+21qq6u1jvvvKM9e/YoNzdXDocjdK7L5VJbW5vmzJmjlStXymq1qqWlhRoVAABGiEQFwJgY6jyR0Zg7EggE5Ha71dbWppSUFNlsNiUkJPR7fqSF7//4j/+o3NxcBQIBnTt3TmvWrAmdV1lZKbvdruXLl9P1CwCAUcTWLwCjbqjbqrZt26aioqKItl/11F/dSGFhYViyYhiGzp07p9OnT+uxxx6TNLTC95KSEnV3d+u2224LvVdlZaXS09Pl8Xjk8/lks9kUFxen8vJy5ebmyul0qqamRsXFxVq8eLFaW1vD5qiUl5eTpAAAMAhWVACMqki2VU2dOlUXLlwY9gqEYRhqaGjQ4cOHw1Y5nnzySfn9fs2cOVNnzpwJ/enu7lZiYqKefvpp7dy5UxUVFWHP66miokJr165VUVGRysrKJA2+esNkegAARgcrKgBGVX19vVwul1JTU3slH9JHKxVbt25VTU2NvF6vFixYMOh5LpdLP/3pT5WamqozZ87o9OnTYQnIbbfdFvZcu92uhoYGLVmyRDNmzNDMmTND/5uYmKg333xTO3fuHFbhe0JCgjIyMpSRkdHnczIzM9XQ0KBdu3bpn//5n7VgwQItWLBAeXl5ys7OZiUFAIAhYo4KgFEV6TyRxYsXD+m8l156SXPmzFFaWppycnJ0++23q6ioSLfddptaWlrCnufxePSJT3xC1157rdLS0nTllVdqzpw5SkxMlCStXr1aklRWVia/39/ne/v9/tAqSl5e3tC/AZLi4uL08Y9/XLfccou2bt2qhx56aMwaAwAAEKtYUQEwqoLzQYY6TySYPAx2nsViUXZ2dq/HV6xYoT179vRZN9KfnJwcORwOuVwurVu3rt9tZ16vVw6Ho8/3HcyZM2cUFxenadOmRfxcAABAogJglAW3SQ11W1VXV9eQzutv7khCQoIKCwtDdSN2u33Qrl/jUfh+5swZTZ8+XfHxLFwDADAcFNMDGFXB1r9DnScyf/58vfnmm6bMHRmrwvdAIKAXX3xRTU1N+uIXvzho4gQAAHojUQEwqsaz69doxbtr1y5VVVWFhk2OpPA92MJ41qxZWrZsmVpaWtTZ2dmrXTIAABgYiQqAUXfxHBWr1drvtqqL56gMdN5Ac1SiTXNzs9xut/Lz80PHgkMh++sUBgAAeiNRATAmxnMyfTTZsWOHkpOT5XA4QsdcLpfa29u1atUq8wIDAGCCIVEBMGaGuq1qtLdfmYkVFQAARgeJCgCMomCNyvTp07V8+fJQu2RqVAAAiAyJCgCMskAgoP/5n//RlVdeOaR2yQAAoDfmqADAKEtISNCUKVP02c9+VldccYXZ4QAAMCExiQwARpnf79f58+c1c+ZMs0MBAGDCIlEBgFF25swZSdL06dNNjgQAgImLRAUARtmZM2c0ffp0xcfzKxYAgOHiX1EAGGWnT59m2xcAACNEogIAo+zMmTOaMWOG2WEAADChkagAwCgKBAJqaWnRe++9p+bmZgUCAbNDAgBgQiJRAYBREhz2eP78eWVnZ8vtdsvpdJKsAAAwDCQqADBK3G63kpKStHbtWjkcDuXn58tiscjtdpsdGgAAEw6JCgCMkra2NqWlpYUdS09PV1tbm0kRAQAwcZGoAMAoSUlJUUtLS9gxj8ejlJQUkyICAGDiijMMwzA7CACIBcEalWnTpikjI0Mej0c+n0+FhYVKSEgwOzwAACYUEhUAGEUXLlzQQw89pMzMTKWmpspms5GkAAAwDFPMDgAAYsn58+c1bdo03XLLLSQoAACMADUqADCKzpw5o2nTppGkAAAwQiQqADCKTp8+rZkzZ5odBgAAEx6JCgCMojNnzmjGjBlmhwEAwIRHogIAo+j06dMkKgAAjAISFQAYRWfOnGHrFwAAo4BEBQBGSSAQ0NGjR3X06FE1NzcrEAiYHRIAABMWiQoAjILgsMfExEQ5HA653W45nU6SFQAAholEBQBGgdvtVlJSku699145HA7l5+fLYrHI7XabHRoAABMSiQoAjIK2tjalpaWFHUtPT1dbW5tJEQEAMLGRqADAKEhJSZHH4wk75vF4lJKSYlJEAABMbHGGYRhmBwEAE10gEFBZWZlmzJihFStWyOPxyOfzqbCwkCn1AAAMA4kKAIyS48eP67HHHtO1116rlJQU2Ww2khQAAIZpitkBAECsOHfunC6//HKtWrXK7FAAAJjwqFEBgFHCVHoAAEYPiQoAjJIzZ86QqAAAMEpIVABglJw+fVozZ840OwwAAGICNSoAMAKGYai+vl7V1dVqbGzUZZddpksuuUQ5OTmKi4szOzwAACYsun4BwDA1NjaqoKBALper12MOh0Pl5eXKzMwc/8AAAIgBJCoAMAyNjY3Kzc2Vz+dTamqqioqKtGTJEh0+fFhlZWXyer2yWCyqra0lWQEAYBhIVAAgQoZhKCsrSy6XS4WFhdq6daumTPnzTlq/369169bJ6XTK4XCooaGBbWAAAESIRAUAIlRXV6eVK1cqNTVVHo8nLEkJ8vv9SktLk9frVV1dnXJyckyIFACAiYuuXwAQoerqaklSUVFRn0mKJE2ZMkXFxcWSpKqqqvEKDQCAmEGiAgAR6ujokCQtWbJkwPMWL14cdj4AABg6EhUAiNDcuXMlSYcPHx7wvNbW1rDzAQDA0JGoAECEVq9eLUkqKyuT3+/v8xy/36+ysjJJUl5e3niFBgBAzCBRAYAI5eTkyOFwyOv1at26db2SlWDXL6/XK4fDoezsbJMiBQBg4qLrFwAMw8VzVKxWq4qLi7V48WK1trYyRwUAgFFAogIAw8RkegAAxg6JCgCMgGEYqq+v17/8y79owYIFWrBggfLy8pSdnc2QRwAARoBEBQBG6Ny5c/rf//1fff3rX9cll1xidjgAAMQEiukBYIR8Pp8uueQSkhQAAEYRiQoAjFBnZ6csFovZYQAAEFNIVABghE6ePEmiAgDAKCNRAYAR8vl8JCoAAIwyEhUAGCG2fgEAMPpIVABghHw+n5KSkswOAwCAmEKiAgAjxNYvAABGH4kKAIzAhQsXdO7cOc2ePdvsUAAAiCkkKgAwAj6fT1OmTNH06dPNDgUAgJhCogIAIxCsT4mLizM7FAAAYgqJCgAMUyAQ0P79+3Xy5Ek1NzcrEAiYHRIAADEjzjAMw+wgAGCiCQQCcjqdmjlzpmw2m1paWtTZ2anCwkIlJCSYHR4AABMeKyoAMAxut1tJSUlau3atHA6H8vPzZbFY5Ha7zQ4NAICYQKICAMPQ1tamtLS0sGPp6elqa2szKSIAAGILiQoADENKSopaWlrCjnk8HqWkpJgUEQAAsYUaFQAYhkAgoEceeUTTpk3TihUr5PF45PP5qFEBAGCUkKgAwDC99957+vnPf66srCylpKTIZrORpAAAMEqmmB0AAExUp06d0hVXXKFVq1aZHQoAADGHGhUAGCafzyeLxWJ2GAAAxCQSFQAYJhIVAADGDokKAAwTiQoAAGOHRAUAholEBQCAsUOiAgDD5PP5lJSUZHYYAADEJBIVABiGQCCgU6dOsaICAMAYIVEBgGE4deqUJGnWrFkmRwIAQGwiUQGAYfD5fJo9e7bi4/k1CgDAWOBfWAAYBgrpAQAYWyQqADAMJCoAAIwtEhUAGAYSFQAAxtYUswMAgInCMAzV19erurpaDQ0NuuqqqyRJOTk5iouLMzk6AABiS5xhGIbZQQBAtGtsbFRBQYFcLlevxxwOh8rLy5WZmTn+gQEAEKNIVABgEI2NjcrNzZXP51NqaqqKioq0ZMkSHT58WGVlZfJ6vbJYLKqtrSVZAQBglJCoAMAADMNQVlaWXC6XCgsLtXXrVk2Z8udds36/X+vWrZPT6ZTD4VBDQwPbwAAAGAUkKgAwgLq6Oq1cuVKpqanyeDxhSUqQ3+9XWlqavF6v6urqlJOTY0KkAADEFrp+AcAAqqurJUlFRUV9JimSNGXKFBUXF0uSqqqqJEmBQEDNzc3asWOHmpubFQgExiVeAABiBYkKAAygo6NDkrRkyZIBz1u8eLEk6f3331cgEJDT6ZTb7VZycrLcbrecTifJCgAAESBRAYABzJ07V5J0+PDhAc9rbW2VJL3xxhv6yU9+ohkzZig/P18Oh0P5+fmyWCxyu929nsfKCwAAfaNGBQAGEKxRsVqtamlpGbRG5YUXXtB7770nm80mh8MROsflcunVV19Vdna2kpOTdemll0qSnE6nkpKSlJaWppaWFnV2dqqwsFAJCQkRxXnxjJeOjg7NnTtXq1evZsYLAGDCIlEBgAEMp+vX/v375Xa7lZ+fHzqvoqJCkhQfH6/29nZ1d3crMTFRc+fO1dq1a0PnVVZWym63KyMjI3QsEAjI7Xarra1NKSkpstlsYYkMM14AALGIRAUABtHY2KiVK1fq5MmTslqtKi4u1uLFi9Xa2trnHJVgjYrFYlF6ero8Ho98Pl9opcQwDHV0dOj555+X3W7vtfKyZ88efepTn1JycrIsFoseffTRflddmPECAIhVJCoAMIju7m794Ac/0COPPKKWlpZej/e1ajHYKogkNTc391p52b59u4K/lk+cOKHz589r4cKFuvfee0PnBFddli9fzowXAEDMIlEBgEEEVznuv/9+7dmzR1VVVaE6kLy8PGVnZw8rARhs5SUQCOjXv/61Fi1a1GvV5eWXX9aUKVP0ta99jRkvAICY1PdQAACAJOn06dPauXOnbrvtNk2dOlU5OTmjdrGfkJCgwsLC0MqL3W4PW3lJSEhQenq63G53WKJy8OBBpaamqry8XNLQZryUlJSoqqoqotgp0AcAmIlEBQAGUFNTI6vVKqvVOiavn5CQoIyMjLDi+YvZbDbt3r1blZWVoVWXU6dOKT8/X7/+9a8lDX3GS3AmjDT8Av0tW7ZQoA8AGBckKgDQD6/XqzfeeEOFhYWmxTDQqkukM17a2tr0xz/+USkpKXr55Zc1d+5cpaWlye12a/fu3UMu0He5XMrNzaVAHwAwpqhRAYA+dHV1qby8XNdff72uu+46s8PpU6QzXrZv364rrrhC+/fv19y5c3XfffeFznvqqae0fPlyCvQBAFGDRAUA+rBz504dPXpUa9euVXx8vNnh9Gk4M17i4uK0Y8cOJScn9yrQ//3vf6+EhAR961vfGnaBPnUtAIDRwtYvAOjh/fff1+uvv66vfOUrUZukSFJcXJzKy8uVm5srp9Opmpqafme8lJeXhxKFlJSUXgX6hw4d0nXXXadt27ZJGnqB/s9//nOdPHlSKSkp6urq0gMPPEBdCwBgVJCoAMBFDMPQH/7wB1177bVKTk42O5xBZWZmqra2NlT4XlJSEvZ4XwlCXwX6J0+e1F133aWKigpJQy/QP3bsmJKTk/X888/rX/7lX3T27FnqWgAAo4JEBQAusn//fn344YfKy8szO5Qhy8zMVENDg3bt2jWkGS+jWaCflZWla6+9VoWFhTp79myfW9A2btwY2oJWUFBAXQsAYEioUQEw6QVb9Xq9Xh04cEBf/vKXtXTpUrPDMkWkBfp1dXUyDEMrV65k8CQAYFRF7+ZrABgHwenwbrdbCxcu1MKFC1VbW6tAIGB2aKbIycmRw+GQ1+vVunXr5Pf7wx4PFuh7vV5ZrVZlZ2erurpa0tDqWiSpqqpqTL8GAEBsYOsXgEnN7XYrKSlJ+fn5kj6q6aisrJTb7e53CGMsi6RAv6CgQE8//XRom9hwBk8CANAfEhUAk1pbW5vS0tLCjqWnp6utrW1SJirS0Av0ly9fLrfbHZpoP9S6lmAdDAAAA6FGBcCk1tzcrKamJq1duzZ0rLKyUna7fdImKkGGYQypQH84dS3UqAAABkOiAmBS++CDD/TjH/9Y6enpWrZsmTwej3w+nwoLC0MrBRjYcAdPAgAwEBIVAJOWYRj65S9/KYvFoiuvvFJtbW1KSUkJterF0DU2Nio3N1c+n09Wq7XfuhbmqAAAhopEBcCkdfDgQdXU1KiwsFDTpk0zO5wJr7GxMVTX0hOT6QEAkSJRATApnTt3Tk6nU5/97Gdls9nMDidmDLWuBQCAwZCoAJiU/vCHP+jDDz/UnXfeyQU0AABRiIGPACadd955R/v379cXvvAFkhQAAKIUiQqASaW7u1u///3v9clPfpJ5HgAARDEGPgITnGEYqq+vV3V1dagmYPXq1crJyWG1oA8NDQ0KBAL62Mc+ZnYoAABgANSoABMYXZYi4/P59OijjyovL09XXXWV2eEAAIABkKgAE9TFcytSU1NVVFSkJUuW6PDhw8yt6Ed1dbWmTZumm266yexQAADAIEhUgAmISeCRO3z4sHbs2KHCwkLNmDHD7HAAAMAgSFSACaiurk4rV65UamqqPB5PWJIS5Pf7lZaWJq/Xq7q6OuXk5Az4moFAQG63Oyams/es27FYLJoyZYruu+8+VpcAAJggKKYHJqDq6mpJUlFRUZ9JiiRNmTJFxcXFKikp0a9+9asBE5VAICCn06mkpCSlpaXJ7XZr9+7dKiwsjChZiYZkZ6C6nd///vfU7QAAMEGQqAAT0Pvvvy9JWrJkyYDnLV68WJJUW1ur//mf/9HUqVNDfxITE0P/3dHRoZkzZyo/P1/SR4X4FRUVeumll7R06dKw502dOlUJCQm9tpJFQ7IzWN2Oy+VSbm4udTsAAEwAJCrAKBtJu+D+LtLPnj2rtrY2vfXWW2pra9Mbb7wh6aO6i4G0trZKkj75yU9q7dq1unDhQq8/XV1deuedd3T99deHPXfZsmX64x//qNbWVnV1denChQvq7u6WJMXHx4clOlOnTtXJkyc1e/bssGRn+/bt+u1vf6vFixf3mSBdnPgEv/7hJjuGYaigoEA+n6/Pup2NGzeG6nYKCgqo2wEAIMpRowKMopG0C+55kX7w4EG99dZbuvTSS/XBBx9o3rx5WrhwoRYuXKg333xTq1atktVqVUtLy4hrVJqbm+V2u0NJhiRVVlbKbrcrIyND0keJQCAQ6DPRuXDhgl577TVlZGTI4XCEXsPlcmnnzp1KTk7uM0kKSkhIUGJios6fP6/k5GTde++9oceefPJJSdLVV1/dZ5IT/PP6669r1apVo1q3AwAAzMOKCjBKhrvtyDAMdXR0qL6+XjNmzAhbkXjyySd12WWXac2aNWGdqpYtWyaHwyGXy6V169b12/XL6/XK4XAoOzt7wNhtNpt2796tyspKpaeny+PxyOfzyWazhc6Ji4vTlClTNGXKlD67ZnV1dcntdoclKh6PR5/97GdDyc7FDMOQ3+8PS1xqa2t7bWez2+3as2ePzp49q87Ozj6TpAsXLuhXv/qVpKHX7VRVVZGoAAAQxUhUgFEQ6baj5557Tm+//XZoK9f58+cVFxenz3zmM2Gva7fb1d7e3isxiIuLU3l5uXJzc+V0OlVTU6Pi4mItXrxYra2tYXNUysvLB93ilJCQoMLCwtC2M7vdHnEh/FCSnZ5fQ2JiohITEzVz5kxJHyVgfSU7N9xwQ5/JzsX27dunP/3pT0Ou2+no6Bjy1wYAAMYfiQowCurr6+VyuZSamtorSZE+upO/detW1dTUyOVy6fvf/75yc3OVkpKi66+/XsnJyTp48GCfF+l2u73P98zMzFRtbW1oq1lJSUnY45FOpk9ISFBGRsagCcFAzx/vZOdi8+bNkzT0up25c+cOOS4AADD+qFEBRsGGDRu0ZcsWlZaWatOmTf2et3nzZpWUlOhb3/qWtmzZEvZYsEbFYrGEXaQPVkhuGIZ27dqlqqqqUPF+Xl6esrOzJ2Sx+HC7fgVny4xW3Q4AADAXKyrAKIi0XfCHH37Y67HhrkjExcUpJycnZi66h7uyk5OTM6p1OwAAwFwkKsAg+rvDf+rUKR05ckStra3yer2SRr7taKTbryaz0a7bAQAA5mLrFyaN4cw36dky+NChQ3r77bd1+eWX68SJE1qwYIEWLVqk9957T7fddhvbjqLASFpEAwCA6EGigklhuBev+/bt08GDB7VmzZrQsSeeeEJz5szR5z73uVA3LsMwlJWVJZfL1WfXr+C2I6fTKYfDwbDBMXZx3c4HH3ygo0eP6hvf+IZuv/12vu8AAEwQJCqIeYPNNwluBwrONzlz5ozeeOMNtba2av/+/br55pt7DTFsb2/XqlWr+n0fq9Xa77ajnnNUMPaef/55TZ8+vVf7ZwAAEL1IVBDTIlnpSE9P17e//W0dP35cV1xxhRYvXqxAIKC3335bd999d+g5PSe2X4xtR9GppaVFL7/8sh544AFWVAAAmCBIVBDTgi1rU1NT5fF4Bq0dKS8v1x133KFZs2ZJGl7L4FhrFxwLurq69JOf/ET33nuvLrvsMrPDAQAAQ0DXL8S06upqSVJRUVGfSYr00TDG4uJilZSUqKmpSffff3/oseG0DI61dsGxIDExUddcc41eeuklzZ49O6L5LAAAwBwkKohqQ+3U1V8L4Ujnm3R0dPR6jJbBE18gENCxY8d06aWXasmSJXK73dq9e/egwzQBAIB5SFQQtfqr99iyZUtYvUfPFsIHDhxQTU2NrrnmGh09elTSyOebYGJzu92aP39+qNbI4XCosrJSbrebBBQAgChFjQqiUiSduuLj43XgwIGwgvfHH39cU6dO1fTp03X77bcz32SS27Fjh5KTk4fUvQ0AAESHeLMDAHoyDEMFBQWhonWPx6NNmzZpzZo12rRpk1paWlRYWCifz6evfOUr+tOf/qT09PSw18jIyFBSUpJuu+02ORwOeb1erVu3Tn6/P+y8YNcvr9crh8Oh7Ozs8fxSMU5SUlLU0tISdszj8SglJcWkiAAAwGBYUUHUibRT17/9278pLS0tbCjjxS2EmW+C4PbA2bNna+nSpTp06JBOnjxJjQoAAFGMGhVEnUg7dXV0dOjkyZOqrKwMayFss9kkSZmZmaqtrQ3Vu5SUlIS9FvNNYt/F3dt+//vfKz09XXfddRdJCgAAUYxEBVEn2HlrqJ26Ojs7B20hnJmZqYaGBuabTGLB7m2nT5/WO++8Q5ICAECUI1GBKfpqJxwfH693331Xp06dkhRZp66htBBmvgmkjxLg3bt3q6urS4mJiWaHAwAA+kGNCkZsqLNOgnq2Ez506JDefvttzZ07V52dnerq6tKGDRvo1DWBRPoZMJNhGHI6ncrNzVVaWprZ4QAAgH6QqGBE+pt1IvVf+9Hc3NyrnfATTzyhefPm6Qtf+IISExOVlZUll8ulwsJCbd26NSxZCXbqcjqdcjgcamhoiLqL4clkOJ8Bs/3pT3/Sa6+9pvb29qhPrAAAmKxIVDBskcw6yczMlN/v1xtvvKEXXnhBn/rUpwacaUGnrokh0s9ANGhsbNQ999yj/fv393osWhMrAAAmIxIVDIthGENe9Vi+fLl++MMfqqWlRZdccomSkpLU3d3dbzvhoIl4p34yieQzEC0rXxMxsQIAYLIiUcGwRDrr5Ec/+pHuuusuXXnlleru7pbT6ZTFYglrJ9zXTAvDMOjUFaUi/QyYXUs0ERMrAAAmM7p+IcxQi6IjnXXy1ltvhaaAXzzTor92wkF06opeg30GDMPQK6+8ooULF8rr9eqb3/ymfvSjH42oDmQkRfv19fVyuVxKTU3tlaRIH31et27dqpqaGrlcLu3atYvPHQAAJiJRQUh/W622bNkS2mq1fPlyud1uNTU1SRr6rJPgbJSgobQTRnQbaN5NX5+lPXv2aOXKlcPetjeUz2fwNftqfx1pcl1VVUWiAgCAiUhUIGnwvfsul0u5ubn6+7//e9ntdiUnJ0uKbNYJYkvwZ9rzMzDUz1IkdSCRvOby5cvD2l+73W7t3r1b7733nqThJ9cAAGCcGZj0uru7DYfDYUgyCgsLja6urrDHu7q6jMLCQkOScc011xjd3d3Gzp07DUmG1Wrtdf7Fz7NarYYko66ubjy+FIyjvj4DkXyWHA6H0d3dPej7RPqajY2NRmVlZdg5jz/+uLFy5UpDklFaWjrg+5WWlhqSjPXr1w/l2wAAAMYIiQpCF5ypqalDTjrG4oIUE0tfn4HhfJYGE+lrbtiwwdi7d2/Y43v37jW+973vkVwDADCBxI/n6g2iUyR79yWpqqpKcXFxKi8v14wZM+R0OpWWlqbNmzeroqJCmzdvVlpaWqizV3l5Od2TYlDwM2CxWEKfgX/6p3+SNPTP0tatW8MeCwQCam5u1o4dO9Tc3KxAIBDx59PtduvgwYNhj3s8Ht1yyy1yOBzyer1at26d/H5/2DnBrl9er1cOh0PZ2dmRfUMAAMCookYFAxZFXyy4d//111+Xy+WSx+PRhg0b9Otf/1oul0slJSVh5zPrJPZlZmaqtrY2VOTu9XolDf2z9OSTT+of/uEflJmZqUAg0GdtyfHjxyN6zSuuuEKnTp1SZWVlWPtru92u8vJy5ebmyul0qqampt9BoiTXAACYj0QF/RZF9xQsjL/mmmvU3t4uu92uO++8U9/+9reZdTKJZWZmqqGhQbt27dI3v/lN7dmzZ8ifpa6uLhUUFKihoUH79u3T7NmzlZ+fL+mjRPcXv/hFKPkZ6mvOmzev3/bXPRMrkmsAAKIXAx8RGtxntVrV0tIS9YP7EL0i/SzNnz9fx48f14YNG2SxWHTbbbfJ4XCEznW5XPrDH/6gDRs2jOrn0+gxSHTGjBmKi4vTd7/7XSUlJY34+wAAAEaOGhUoJyeHvfsYFZF+lr72ta9Jknw+n26++Wa1tLSEne/xeLRq1apR/3wGB4k+9NBDKisr03//93/rC1/4gvbt2zfC7wAAABgtrKhAUvicCqvV2u/e/UhmXyB29TVQMSEhQdJHn6WPfexj6urqGvSzdODAAa1du1ZFRUX62c9+FmrAcHFtSWFhofbv3z/mn89jx47pN7/5jdatW6fExMTR/HYBAIBhIFFBSH+TvyX27sc6wzBUX1+v6urqUJ3R6tWrlZOT06vOqGfRe0tLizo7O3XHHXfoyJEjOnz4sP7jP/5De/bs6fO9Lv4sbd68WSUlJVq/fr0eeuihQROgsfx8Goah8vJyfexjH1NGRsawXwcAAIwOEhWE6bl3n8L42BdpAtDc3Cy32x0qepekxx9/XMeOHVN6erqWLFmid999V1/84hc1f/583XPPPers7Oz1WRpO3dNYfz737dunxsZG3XfffXzeAQAwGV2/ECa4d59i+cnh4i1/qampKioq0pIlS3T48GGVlZXJ5XIpNzc3tKUqEAjowIEDSk9PD3udjIwMzZs3T7fccoukjxIKh8Mhl8uljo4Obd26NawIfrh1T2P9+bTZbKqtrdXbb7+tlJSUMXkPAAAwNKyoAJOUYRjKysqSy+VSYWFhv8mE0+mUzWbT97//fXm9Xl24cEELFizQPffcEzq3srJSdrs9bMvUM888ozVr1qi7u3tC1T3V1NTo9OnTuu2228wOBQCASY1EBZikgq2EU1NT5fF4Bm37+5Of/ER5eXm67LLL9Oijj/ZZ9H5xPUlwpWbq1Km6cOFCr9eOj4/XU089pbvuumvMv9ZIdHR0qLy8XMXFxZo1a5bZ4QAAMGnRnhiYpKqrqyVJ/1979x/cdn3fcfzl2HEbSJyYBJwgfkiV7WDHMd+kDGY1obC1c4/bD7s0CYFtrs6eb8eu17G1xx/xHX8s2Q6aje6uvV2DNpGtQHBb5AIrC2sbIMgOxzCKIiJHsplKMdj8MlZ+kBAp3/2Rkxbh3z8kfS0/H3f889UnysdK7+qX3p/P+93a2jpuSJGkkpIStbW1SZLefPNNrV27ViUlJXK73aqtrU0P/rw0pJimqZaWlnR4OXXqlPx+v77zne+otbVVf/M3f6M77rhDFy5c0J49e2S170rKy8t13XXXKRgM5nsrAAAsatxRARapkZERSVJlZeWk65xOZ8Z6SSouLlZdXd243bG6u7sVCATkcDjSx8k+e68kVakJBALq6emx3J2oTZs26eDBg7rlllvSAQwAAOQWFRVgkSovL5ck9ff3T7puYGAgY/1UZlqp8fl803rfXLLb7SopKdHzzz+vgwcPKhQKKZlM5ntbAAAsKgQVYJFqamqSJHk8njHT3lMSiYQ8Ho8kqbm5eVrvO5dKjVVcuHBBp06d0kcffaSKigqFw2F5vV7CCgAAOURQARYpl8slwzAUi8XU3t4+JqzMtoVwtio1uRQOh3X11VfrnnvukWEY2rZtm8rKyhQOh/O9NQAAFg26fgGL2KXduearhXCqm5jdblc0Gp2ym9h0hz3m0sGDB1VRUSHDMNLPAoGAhoeH1djYmL+NAQCwiBBUgEVuppPppzKT+SyGYai3t9dyU+BDoZDC4bC2bduWfjberBgAAJA9BBUAMk1TPT098vl8GhkZUXl5uZqbm9XQ0DCrEJGNSk0uJZNJeb3e9KyYcDis06dPZ7RhBgAA2UVQAZAV812pybVkMqlwOKxQKKR33nlH3/rWt7R06dJ8bwsAgEWDoAIga+a7UpMPFy5ckMfj0e23366qqqp8bwcAgEWDoAIAU3jllVf0m9/8Rtu3b8/3VgAAWDRoTwwAU9i4caPeffddffDBB/neCgAAiwZBBQCmcNlll2n9+vXj3rcBAADZQVABgGnYtGmTjh8/rrNnz+Z7KwAALAoEFQCYhoqKCq1Zs0ZvvPFGvrcCAMCiQFABgGnatGmTAoGA6EECAED2EVQAYJqqq6t1/vx5xWKxfG8FAICCR3tiAJiB7u5uvfvuu1q7dq26urrS82GamprkcrkWzHwYAACsjqACADNw5MgRfeMb39Dg4OCY1wzD0P79+1VfX5+HnQEAUFgIKgAwTcFgUFu3blU8HpfD4VBra6sqKyvV398vj8ejWCymsrIyHT58mLACAMAcEVQAYBpM09TmzZsVCATkdru1b98+lZSUpF9PJBJqb2+X1+uVYRjq7e3lGBgAAHNAUAGAafD7/dqyZYscDocikUhGSElJJBKqqqpSLBaT3++Xy+XKw04BACgMdP0CgGno6uqSJLW2to4bUiSppKREbW1tkiSfz5errQEAUJAIKgAwDSMjI5KkysrKSdc5nc6M9QAAYHYIKgAwDeXl5ZKk/v7+SdcNDAxkrAcAALNDUAGAaWhqapIkeTweJRKJcdckEgl5PB5JUnNzc662BgBAQSKoAMA0uFwuGYahWCym9vb2MWEl1fUrFovJMAw1NDTkaacAABQGun4BwDRdOkfFbrerra1NTqdTAwMDzFEBAGCeEVQAYAaCwaBaWloUCATGvMZkegAA5g9BBQBmyDRN9fT06MEHH1RxcbGcTqeam5vV0NDAkEcAAOYJQQUAZumZZ57RunXrdNNNN+V7KwAAFBwu0wPALJWWlurcuXP53gYAAAWJoAIAs1RaWqrz58/nexsAABQkggoAzBIVFQAAsoegAgCzVFpaqk8//TTf2wAAoCARVABgljj6BQBA9hBUAGCWOPoFAED2EFQAYJaoqAAAkD0EFQCYJSoqAABkD0EFAGaJy/QAAGQPQQUAZomjXwAAZA9BBQBmqbS0VMlkUolEIt9bAQCg4BBUAGCWSktLJYnjXwAAZAFBBQBmqaSkREVFRQQVAACygKACALNUVFSkz33ucwQVAACygKACALOUTCZ17tw5vfTSSwqFQkomk/neEgAABYOgAgCzkEwm5fV6VVFRocrKSoXDYXm9XsIKAADzhKACALMQDoe1cuVK3XPPPTIMQ9u2bVNZWZnC4XC+twYAQEEgqADALAwODqqqqirjWXV1tQYHB/O0IwAACgtBBQBmwWazKRqNZjyLRCKy2Wx52hEAAIWlyDRNM9+bAICFJnVH5bLLLlNtba0ikYji8bjcbreKi4vzvT0AABY8ggoAzFIymdSTTz6pM2fO6Hd/93dVU1NDSAEAYJ6U5HsDALBQFRcX6/rrr9fp06dVV1eX7+0AAFBQuKMCAHOwdOlSBj4CAJAFBBUAmAMm0wMAkB0EFQCYg9LSUoIKAABZQFABgDng6BcAANlBUAGAOeDoFwAA2UFQAYA54OgXAADZQVABgDkgqAAAkB0EFQCYg9LSUiWTSSWTyXxvBQCAgkJQAYA5KC0tlSSqKgAAzDOCCgDMQUlJiYqKiggqAADMM4IKAMxBUVER91QAAMgCggoAzBFBBQCA+UdQAYA5IqgAADD/ikzTNPO9CQBYiEzTVHd3t/7hH/5By5Ytk91uV1NTk1wul4qKivK9PQAAFjSCCgDMQjAYVEtLiwKBwJjXDMPQ/v37VV9fn/uNAQBQIAgqACwtVbXo6urSyMiIysvL8161CAaD2rp1q+LxuBwOh1pbW1VZWan+/n55PB7FYjGVlZXp8OHDhBUAAGaJoALAsqxYtTBNU5s3b1YgEJDb7da+fftUUlKSfj2RSKi9vV1er1eGYai3t5djYAAAzAJBBYAlWbVq4ff7tWXLFjkcDkUikYyQkpJIJFRVVaVYLCa/3y+Xy5Wz/QEAUCjo+gXAckzTVEtLi+LxuNxutyKRiHbt2qUdO3Zo165dikajcrvdisfjamlpUS6/b+nq6pIktba2jhtSpItDINva2iRJPp8vV1sDAKCgEFQAWE53d7cCgYAcDseYo1XSxSCwb98+2e12BQIB9fT05GxvIyMjkqTKyspJ1zmdzoz1AABgZggqACzHylWL8vJySVJ/f/+k6wYGBjLWAwCAmRn/NwAAyKOZVi0+/PBD+f3+rHYGM01T//u//6vly5dLkjwej+6///4J76h4PB5JUnNz87z8/QAALDYEFQCWM9OqxU9/+lN5vd6M1/bu3TsvncGSyaROnDihV199VWfOnFFjY6N8Pp+OHj2q9vb2Cbt+xWIxGYahhoaGWf/dAAAsZnT9AmA5qc5adrtd0Wh0wqrF9ddfr3feeUeS5tQZLJlMKhwOa3BwUDabTTU1NUomkzp27Jhee+01FRcX63d+53dUW1urkpKSjI5kdrtdbW1tcjqdGhgYSP+9n//85/Xcc8/ptttuy9bHBABAQSOoALCc6cwq+Yu/+As9+uijkjSneSbJZFJer1crV65UVVWVIpGI3n77bZWWlmrVqlW6+eabVVlZqSVLMq/0TTXj5W//9m8Vj8e1bds2XXnllfPzwQAAsIgQVABY0nSqFpLmPM8kFAopHA5r27Zt6WePPfaYrr76at12222T3nExTVM9PT3y+XzpuzHNzc1qaGhQUVGRenp69Prrr2v79u1as2bN3D8UAAAWEe6oALCk+vp6HT58OF216OjoyHi9rKxM8Xh8Wp3BOjo65PP5xg0qkUhEVVVVGc82bNig4eHhKS/iFxUVyeVyTTjQMXU/pbOzk7ACAMAMEVQAWFZ9fb16e3vHVC3++I//WA8//LB8Pl+6M5hpmuru7h7T+esLX/iCpMx5JqZp6q233tKrr76qaDSqc+fOyTCM9OuRSES1tbXz8jMQVgAAmB2CCgBLm6hq8fTTT0u62Blsovsie/fu1bp16yRd7CR24cIFRaNRvfrqqxodHdWmTZvU2NioJ598Up2dnaqurlYkElE8HldNTc28/QyXhpVvfOMbeu+99zIu7hcXF8/b3wUAQKHgjgqABSnVGezqq6/WqVOnFI/HJ+z8JUl/93d/p3Xr1imZTOqmm27Sxo0btXTpUknjd/3KRnjw+/06dOiQqqqqtH79ekWjUY2OjsrtdhNWAAD4DIIKgAXJNE1t2rRJR48elTR156/rrrtOzz33nNavX5+3UBAKhXTs2DHt3Lkz/ayzs1O1tbWqq6vLy54AALCqJVMvAQDrKSoq0n333SfpYuevz4YU6eJl+n379slut+utt97Sxx9/nNfKxeDg4JgjZdXV1RocHMzTjgAAsC6CCoAFKxQKSdK0On9Jks/ny9nexmOz2RSNRjOenThxQjabLU87AgDAuggqABasVCevVOeviTidzoz1+VJTU6PR0VF1dnYqEAjoiSeeUDQa1VVXXZXXfQEAYEUEFQAL0vnz53X+/HlJFzt/TWZgYEDSxc5f+VRcXCy3263a2loNDw9r48aNuu222/Szn/1MH3zwQV73BgCA1XCZHoClfbYj17XXXqtgMKhgMKihoSHt3r1bdrtd0Wh01tPp8627u1tHjx7V9u3btXr16nxvBwAASyCoALCsZDIpr9erlStXqqqqSsePH1d/f782btyom266STabTV/84hcVCASm7PplGIZ6e3unnDafL4QVAAAyEVQATGqiie8ul2tWv/TP5P2CwaCOHz+uu+66K/3sySef1IYNG9LtfIPBoLZu3ap4PC673a62tjY5nU4NDAyk56iUlZXp8OHDqq+vn9uHkWV+v1/BYDAdVub7swcAYCEhqACY0EQT3yXJMAzt379/Rr/8T/f9Pv30U4VCIf3617/WV77yFRmGkV4XCAQ0PDysxsbGrO0zX1LBJBgMasOGDfr2t7+94H8mAABmi6ACYFyXViommvg+k0rFdN/v4Ycf1pkzZ7Rq1SqtXr1aJ0+e1Pbt29PvM9GARNM01dPTI5/Pl64+NDc3q6GhYUFVH0zT1L//+7/rL//yL3X27Nlpf/ZUXwAAhYagAmAM0zS1efPmGd/9+OzF95qaGhUXF8/o/RwOh1588UVdc801unDhgrxer8rKylRdXa1IJKJ4PC63253XwY3ZNNFnlQoiTz31lJ555hlFo1FVVlbqxIkTCoVCBVFRAgDgUgQVAGP4/X5t2bJFDodDkUhkWt20brnlloyL79FoVKOjo3K73Tpy5MiM3y/VnWui8FOoxvvsJzvadt111+mjjz7SqVOn5qXyBQCAVYw/yhnAotbV1SVpehPfOzo69OCDD+qOO+7Q8uXLtW3bNkkXv8n/8Y9/rB/84Ad67rnnZvR+Pp8vHVSKi4tVV1c35qhXofrsZz/ZkblHHnlEv/nNbyRp3ErV/fffn65UtbS0WLrrmZVwjA4ArIGgAmCMmU58Ly4u1vLly7Vhw4aM1+vq6hSJRLRq1aoZvV++J8jn06WfvWmaamlpSR93+2wQ2bJli2677TY5HI4xr0kXw9++fft06NAhBQIB9fT0WHKOjJVMVL3au3cvx+gAIMeYTA9gjNQE9+lOfHc6nbrxxhsVjUYzXo9EIqqtrdW11147o/fL9wT5fLr0s+/u7lYgEJgwiDz77LOSplepkiSfz5fFnS98qepV6jPfvXu3Dhw4kB4qGggEtHXrVgWDwXxvFQAWBYIKgDGampokSR6PR4lEYtw1iURCHo9HktTc3KyamhqNjo6qs7NTgUBAnZ2disfjqqmpmdX7LVaXflZPPfWUpImDyEwrX319fRoYGNCZM2fmcceF4bPVq0gkol27dmnHjh3atWuXotGo3G634vG4WlpaxPVOAMg+ggqAMVwulwzDUCwWU3t7+5hwkerSFYvFZBiGGhoaVFxcLLfbrdraWg0PD6u2tjbdnWs277dYXfpZPfPMM5ImDiIzrXx97nOf00svvaR/+Zd/0SOPPKJnnnlG//M//6O3335b58+fn8efYuGZqnqVOkaXqqz09PTkaacAsHjQ9QvAuOZz4nsymVRXV5f+9E//VGfPnl3wE+Sz7dLPXpJ2796tXbt2jVmX6hBmt9sVjUan3U3t7NmzGh4e1tDQkN59910NDQ3pzJkzWrNmjdauXat169Zp7dq1Wr16tZYs+f/vswq5A9t3v/td7d27d8LPOmXPnj3q6OjQd77zHX3ve9/L4Q4BYPEhqACY0HxMfE8mk/J6vVqxYoWKiop0//33KxaLzfr9FotgMKg777xT/f39EwYR0zS1adMmHT16dEbzbj7LNE2dPHkyI7gMDw9LkioqKrR27VpVVFTohRde0BVXXKHq6uqM9tMLPaxcuHBBLS0t+vGPf6wDBw5ox44dE649cOCAdu7cqdbW1vRRRQBAdtD1C8CE6uvr1dvbO6eJ7+FwWGVlZenp8tu2bdPu3bv15ptvqri4eMFOkM+2+vp6nThxQjfccIOi0aja29vHBJFkMim73a6jR4/K6/Xq0KFDE1aq9u/fP+HnW1RUpLKysvRgTeniL+8fffRROrj893//t8rLy9P/joZh6MCBAzp69Kg2b948p591PtsBT1b1MU1Tp0+f1gcffJDx34cffqjBwUFJNHwAACuhogIgqw4ePKiKigoZhpF+FggENDw8rMbGxvxtbIGYzhG8yy+/XNdcc41OnDgx5s/PV6Vqon/HZ599VjabTWvXrk0fG7vyyiunXWWZj6pdSqp6lxo6euLECQ0NDam2tlYjIyN6//33dfbsWa1atUpr1qzJ+O+NN97Ql7/85Wkfo3v55Zf1pS99aVr7AgDMDkEFwLwZ75vx2tpalZSU6M/+7M/S6zo7O1VbW7tohjjOVTAY1D333KNQKDTmtdQv8xs3bpxT5WsqoVBI4XA4PdBTuvjv6HQ6tWrVKg0NDaX/O3/+vK666qqM+y6rVq0as4/JhllO995SMpnURx99pA8++EDHjh1TIpHQ3XffnX79scce05IlS1RXV6crr7xSq1ev1tKlS8e8j2ma2rx5swKBwJTH6K655hr90z/9k26//XatWbNmrh8tAGACBBUA82Kyb8bXrVun++67T1/96lcViUTSLWAX+t2GXEkmk3rmmWf04osv6uOPP9aSJUt0xRVX5PTIXKpakToeNtG/o2ma+vjjjzPuu7z33ntaunRpuuqS+m/Lli3TCgaGYei1115TPB4fc2xrZGRExcXFWrNmjU6ePKlbbrll1tW76TaQ+OUvf6nTp0/r6NGjqqurk8vlUmlpacE2GgCAfCGoAJiz6Xwzvnz5cj300EPaunUrv8TNQCogXHbZZaqtrc3rJfbZdv1KJpN6//3308FlaGhIr732mn74wx/K4XAoEolMedTqr//6r3XdddfpiiuuGHNsq6ysTEVFRRNWfWZSvZvJUbQPP/xQL774ot555x0lk0mtW7eu4BoNAEA+EVQAzMlMjsxM1nkK45uPX76t6L777tP3v//9abcD/qu/+iv98z//86S/+E+36jMV0zRndIzuV7/6lYaGhnTPPfeknxXCvxEA5BtdvwDMyXQH5R06dCg9KM/lcuVptwvP4OCgqqqqMp5VV1drcHBwQf8SfPLkSUkTD7NMcTqdkqSzZ89OGTZSQ0dTVZ/a2tpZVe+Kiorkcrmm/b/TRCKhDRs2ZDwrhH8jAMg3JtMDmJOuri5JUmtr67jHd6SLYaWtrU2S5PP5crW1gmCz2dTX15fxLBKJyGaz5WlH8yPV3ne+2wEXFxerrq5OjY2Nqqury8nRK5vNpmg0mvHs2LFjGhkZ0blz57L+9wNAoSKoAJiTkZERSdP/Zjy1HpMzTVN+v19er1cPP/yw/vAP/1CPPvqonnzyScXjcdXU1OR7i3PS1NQkSfJ4PEokEuOuSSQS6aGKzc3NudrajNXU1Gh0dFSdnZ0KBALq7OzUqVOnVFRUpH/7t39TMBjUhQsX8r1NAFhwOPoFYE6y9c34YjbRhe7//M//1A033KDHH398wV/SdrlcMgxDgUBg3GGWqbtNsVhMhmGooaEhj7ud3ERHzpYsWaL+/n69+OKLCgQC+r3f+z1dc801+d4uACwYXKYHMCd+v19btmyZ9qA8v9/PHZVJzMdskYViOu2AP//5z+vnP/+5/uAP/iDf2521RCKh1157Ta+88oocDoduvfVWLV++nHbGADAFggqAOaHr1/xZjJ/lVO2AOzo69O677+quu+5a8MMVT506pZdffll9fX26cOGCrrnmGtoZA8AkCCoA5my6g/IKoQqQTanq1HRnixRKdWqqdsDd3d06duyY7r77bq1YsSLf252zw4cP66233qKdMQBMgTsqAOasvr5ehw8fTn8z3tHRkfH6ZwflYXwz6aDW0dEhn89XEEFlqnbADQ0NOnnypH72s5/pxhtv1Hvvvbegj0udOXOGdsYAMA0EFQDzor6+Xr29vTMalIdMdFAbX1FRkW6//XY9/PDDOn78uDZs2KBwOKwjR44syONSNptN4XBYhmGknx07dkwXLlzQyZMnC6JqBADzgaACYN7MdFAeMtFBbWKRSEQOh0Pbt2+XdLFK19nZqXA4vOCqEDU1NTpy5Ig6OztVXV2tSCSi06dP6+qrr5bX69XNN9+sL37xi1q6dGm+twoAecUdFQCwCDqoTezgwYOqqKjIqEIEAgENDw+rsbExfxubpWQyOW7Xr7ffflsvvPCCPvnkE335y19WVVUV1UgAixZBBQAsYjF2/ZquUCikcDisbdu2pZ898cQT2rhx44KrqEzFNE298cYbOnz4sK644grdfvvtWr16Ne2MASw6BBUAsBA6qI0vmUzK6/WqrKxM1dXV6uvrUzQa1fbt27V+/fp8by8rzp07p1deeUW9vb1KJBK67rrraGcMYFEhqACAxbz++uvauXOnTpw4Mea1xdxB7bPHpUpLS/Vf//VfuvPOO2Wz2WSaprq7u9XV1ZVu5tDU1CSXy7WgK0+vvPKKBgYGdPfdd6ef0c4YwGJAUAEAC0lVDi6//HKdO3dOTz31lEZGRnTzzTfrzjvvpIPaZxw9elQvv/yyNm7cqG9/+9sTDo5cyOGu0O7nAMB00fULACwkHA5r5cqV6bsY3/zmN/n2fBI33nijXn/9dX3ta1/TJ598IofDodbWVlVWVqq/v18ej0eBQEBbt25dsMflxmtnHAqFdObMGX344YdavXp1xvpCrSwBWHyoqACAhfDt+cwshgYEn72fE4lE9PHHH8vpdCoUCmnjxo1qaGjQsmXLFAwG04NXP2uhV5YALD4EFQCwkFAopOPHj6fnhUjcR5hMqqWzw+FQJBIp2JbOE7Uz/vDDD/XCCy9oaGhIa9asUXt7u+Lx+LiVpcXaiAHAwsXRLwCwgNRxnaeeeko9PT3av3+/tm3bpmXLlunkyZOqqanJ9xYtqaurS5LU2to6bkiRpJKSErW1tamjo0M+n29BBpXi4mLV1dWNCaurV6/WnXfeqYGBAd1+++2Kx+PjVpbuv//+dGWppaVlQVaWACw+BBUAyLOJjuv84he/0A033KDHH3+cNrQTGBkZkSRVVlZOus7pdGasLzRDQ0P67W9/K4fDMSakSBfD2r59+3To0CEFAgH19PQsyMAGYHFZku8NAMBilpqbEggE5HA4tHv3bh04cEC7d++W3W5XX1+fbrvtNgWDwXxv1ZLKy8slSf39/ZOuGxgYyFhfaGZSWZIkn8+Xq60BwKwRVAAgT0zTVEtLS/q4TiQS0a5du7Rjxw7t2rVL0WhUbrdb8XhcLS0t4krhWE1NTZIkj8ejRCIx7ppEIiGPxyNJam5uztXWcorKEoBCRFABgDzp7u5OV1ImO65jt9vTx3WQyeVyyTAMxWIxtbe3jwkrqa5fsVhMhmGooaEhTzvNLipLAAoRQQUA8oTjOnNXVFSk/fv3q6ysTF6vV1VVVdqzZ48OHDigPXv2qKqqSl6vV8uWLdMPfvCDgr1ATmUJQCHiMj0A5AnHdeZHfX29Dh8+nG5I0NHRkfG6YRi69957dezYMVVVVem9994b0+Z3oUtVlgKBgNrb2yecJ1PolSUAhYWgAgB5wnGd+VNfX6/e3l719PTI5/OlJ7I3Nzenfynv7u7Wj370I1VXV2v9+vUKh8M6cuSI3G73gg8rqcrS1q1b5fV6dejQIbW1tcnpdGpgYCBjjsr+/fsLtrIEoLAw8BEA8iQ1rNButysajRbssEKrCIVCCoVCuuuuu9LPCm2YJpPpARQSKioAkCcc18mtwcFB3XDDDRnPqqurNTg4WDBBZarKEpUUAAsJFRUAyKPUHJV4PC673T7hcZ3Dhw/zTfgchUIhhcNhbdu2Lf3siSee0MaNGwsmqABAISGoAECecVwnN5LJpLxer8rKylRdXa2+vj719/dr69atuvXWWyVdvMfS1dWVrkQ0NTXJ5XJRiQCAPCCoAIAFJBIJ/eM//qOef/55rVq1Sna7XXfeeSfHdeZZMplUOBxOd/1au3atnnrqKV24cEE/+tGPCIsAYCEEFQDIs9Q3/ZdffrlqamoUjUY1OjpaEN2oFoKenh79/u//vj755BM5HA61traqsrJS/f39HL8DgDwiqABAno13d6LQulFZlWma2rx5swKBgNxu94QNDbxerwzDUG9vLxUuAMgRJtMDQJ4NDg6qqqoq41mqGxWyq7u7W4FAQA6HY0xIkaSSkhLt27dPdrtdgUBAPT09edopACw+BBUAyDObzaZIJJLxLBKJyGaz5WlHi0dXV5ckqbW1ddw5NtLFsNLW1iZJ8vl8udoaACx6zFEBgDyrqamR3+/Xf/zHf2jjxo2KRCKKx+OqqanJ99YK3sjIiCSpsrJy0nVOpzNjPQAg+wgqAJBHpmnqyJEj6u3tVSAQ0LXXXqs/+ZM/0Te/+U0u0udAeXm5JKm/v3/SdQMDAxnrAQDZx2V6AMgT5qfkn9/v15YtW2S32xWNRsc9/pVIJFRVVaVYLCa/3y+Xy5WHnQLA4sMdFQDIg9RE+tRF7t27d+vAgQPavXt3+uL21q1bFQwG873VguZyuWQYhmKxmNrb25VIJDJeT3X9isViqq2t1YoVK3Tw4EGFQiElk8k87RoAFgcqKgCQY7NpiSsxNT1bUqExHo/Lbrerra1NTqdTAwMD6Tkqy5cv15//+Z/r1ltv1fr165l1AwA5QFABgBxLHTdyOByKRCJTHjfas2ePfvKTn3BELIumOobX0dGh8+fP66677ko/Z9YNAGQXl+kBIMdm0hK3o6NDDzzwgBKJxLhT01NHxJiaPjf19fXq7e1VT0+PfD5fumrV3NyshoYGPf/886qoqMj4M9XV1Xr77bc1OjpKpQsAsoCgAgA5Nt2WuF/4whckXayujHdE7P77708fEWtpaWFq+hwVFRXJ5XKNe1neZrMpHA7LMIz0s4MHD+rRRx9VX19fxtq9e/dS6QKAecBlegDIsem2xD106JAkMTXdAmpqajQ6OqrOzk4FAgE9+OCDeuCBB9TX10czBADIEoIKAORYU1OTJMnj8YzpMpWSSCT05JNPSmJquhUUFxfL7XartrZWQ0NDevTRR3Xu3Dm53W5FIhHt2rVLO3bs0K5duxSNRuV2uxWPx9XS0iKuggLA7BBUACDHptsSNx6PS2JqulUUFxerrq5OK1asSFdSqHQBQPYQVAAgx4qKirR//36VlZXJ6/WqqqpKe/bs0YEDB7Rnzx5VVVXJ6/Vq6dKlkpiabjUzaYYgUekCgNkiqABAHtTX1+sXv/iFbDabYrGYOjo6tHPnTnV0dCgWi8kwDD3yyCOSpj4i5vF4JEnNzc052/9iNt1mCFS6AGBu6PoFADmWTCYVDocViUT0wAMP6IYbbtDTTz89piWuJH3/+99XIBBQe3v7hIMhU8Em9WeQXdNthkClCwDmhoGPAJBDyWRSXq9XZWVlqq6uVl9fn06dOjXhhPPpTE0vKytjjkoOpQZ22u12RaPRKQd2+v3+cVseAwAmR1ABgBwKhUIKh8Patm1b+tlUE86nmprOvI7cMk1TmzdvViAQGHe+TarS5fV6ZRgG820AYJYIKgCQQwcPHlRFRUXG4MBAIKDh4WE1NjZO+OdM05xwajq/BOcelS4AyD6CCgDkUCgU0vHjx7V9+/b0s6kqKrAmKl0AkF0EFQDIoWQyqR/+8Ie64oorVFdXp0gkong8PuEdFVgblS4AyB6CCgDk2E9+8hOVlJTosssuk81mU01NDSEFAIDPoD0xAGSRaZrq7u5WV1eXRkZGdNlll0mS9uzZoxUrVuR5dwAAWBcVFQDIEu4wAAAwewQVAMiCS7tCORwOtba2qrKyUv39/XSFAgBgGggqADDPmLMBAMDcEVQAYJ6lJpc7HA5FIhEmlwMAMAtL8r0BACg0XV1dkqTW1tZxQ4oklZSUqK2tTZLk8/lytTUAABYMggoAzLORkRFJUmVl5aTrnE5nxnoAAPD/CCoAMM/Ky8slSf39/ZOuGxgYyFgPAAD+H0EFAOZZU1OTJMnj8SiRSIy7JpFIyOPxSJKam5tztTUAABYMggoAzDOXyyXDMBSLxdTe3j4mrKS6fsViMRmGoYaGhjztFAAA66LrFwBkwaVzVOx2u9ra2uR0OjUwMMAcFQAApoGgAgBZYJqm/v7v/14PPfSQ4vH4mNeZTA8AwOTG75sJAJi1YDColpYWBQKBMa+Vl5ertbVVDz30EEMeAQCYBBUVAJgHpmmqu7tbjzzyiB5//HGdP39e69at07333quqqir19/enj3wtX75cfr+fagoAAJMgqADAHE1WQbn0iFfqEr3X65VhGOrt7aWqAgDABAgqADAHl16adzgcam1tVWVlZUYF5dJL84lEQlVVVYrFYvL7/XK5XPn+EQAAsCTaEwPALJmmqZaWFsXjcbndbkUiEe3atUs7duzQrl27FI1G5Xa7FY/H1dLSItM0VVJSora2NkmSz+fL808AAIB1EVQAYJa6u7sVCATkcDi0b98+lZRk9icpKSnRvn37ZLfbFQgE1NPTI0lyOp2SpJGRkZzvGQCAhYKgAgCz1NXVJUlqbW0dE1JSxqugDAwMSLrYAQwAAIyPoAIAs5SqiFRWVk667tIKSiKRkMfjkSQ1Nzdnd4MAACxgBBUAmKVURaS/v3/SdakKysqVK9Xe3q5YLCbDMNTQ0JD1PQIAsFDR9QsAZsnv92vLli2y2+2KRqPjHv+6tMvX2rVrNTQ0lNEFDAAAjI+KCgDMgmmaWrFihWw2m2KxmNrb25VIJDLWpOamxGIxSdLQ0JBsNpu6uroIKQAATIGKCgDM0Keffqpf/epXisVicjgc2rFjh+LxuOx2u9ra2uR0OjUwMJCeo1JaWqqdO3eqvb1dktTX16fNmzdreHhYNptNNTU1Ki4uzvNPBQCAtRBUAGAKyWRS4XBYg4ODWrFihfr6+rR8+XLdcccdWr58+bQn00vS+fPntXfvXl1//fWqra1VNBrV6Oio3G43YQUAgEsQVABgEslkUl6vVytXrlRVVZVCoZDee+89fetb39LSpUvT60zTVE9Pj3w+n0ZGRlReXq7m5mY1NDSoqKgovS4UCumNN97Qjh070s86OztVW1ururq6nP5sAABYGUEFACYRCoV0/Phxbd++Pf1sLsHi4MGDqqiokGEY6WeBQEDDw8NqbGycjy0DAFAQuEwPAJPo7+9XdXV1xrPq6moNDg7O6v1sNpui0WjGs0gkIpvNNus9AgBQiMYfpQwABc40TXV3d6urqyt9VKupqUkulyt9VOuNN95QX1+fPvnkk4wKSCQSUW1t7az+3pqaGh05ckSdnZ2qrq5WKBTSBx98oK9//evy+/2T7gcAgMWEo18AFp2pLr97PB69//77evPNN/W1r31Nv/71r1VWVqbq6mpFIhHF4/E5XX6/9HL+smXL9Oyzz+q5555TKBQadz+XXsYHAGCxIKgAWFSCwaC2bt2qeDwuh8Oh1tZWVVZWqr+/P91OeNmyZXrggQd07733asWKFRnBYr7bCQeDQblcLp0+fXrC/TAgEgCwGBFUACwapmlq8+bNCgQCcrvd2rdvX8Y0+dSARq/XK8Mw1Nvbm9VjV1bbDwAAVkJQAbBo+P1+bdmyRQ6HQ5FIJCMUpCQSCVVVVSkWi8nv98vlci2a/QAAYCV0/QKwaHR1dUmSWltbxw0FklRSUqK2tjZJks/nW1T7AQDASggqABaNkZERSVJlZeWk65xOZ8b6xbIfAACshKACYNEoLy+XdHE2ymQGBgYy1i+W/QAAYCUEFQCLxh/90R9JkjwejxKJxLhrEomEPB6PJKm5uTmr+2lqarLUfgAAsBKCCoCCZZqm/H6/vvvd76q1tVV79uzR2rVrFYvF1N7ePiYcpLpsxWIxGYahhoaGrO7P5XLJMAzL7AcAACuh6xeAgjTZUMclS5bowoULstvtamtrk9Pp1MDAQF7mllw618UK+wEAwCoIKgAKznSGOhYXFyuZTI75s/mYBD9ZqHI6nXrwwQfV1NQ0b0MmAQBYCAgqAArKTIYorl27Vo2NjSopKVF5ebmam5vV0NCQl6GKpmmqp6dHPp9PH330kWKxmL70pS+publZ/f39Gh0dldvtJqwAABYNggqAgjLTIYovvfSStm7dmoedTiwUCun48ePavn17+llnZ6dqa2tVV1eXx50BAJA7XKYHUFBmOkTx6aefztXWpm1wcFDV1dUZz6qrqzU4OJinHQEAkHvj/784ACxQhTBE0WazKRwOyzAMmaap7u5uPfjgg1q6dKl++ctfqqmpSS6XKy9H1AAAyBWCCoCCUghDFGtqanTkyBF973vf07/+67/qxIkTGa/v3bs3L5f+AQDIJe6oACgoqTsqdrtd0Wh0yjsqfr9fLpcrDzud3Ouvv65bb71Vp06dmrBzGW2LAQCFjKACoKDMpOuXYRjq7e213BGqQvgZAACYK4IKgIKz0IcozrRzmVWrQgAAzAVdvwAUnPr6eh0+fFiGYSgWi6mjo0M7d+5UR0eHYrGYDMOwbEiRZt65zOfz5WprAADkDJfpARSk+vp69fb2pocojoyM5H2o43QVQucyAADmiqACoGAVFRXJ5XItuGNRhdC5DACAueLoFwBYTFNTkyTJ4/EokUiMuyaRSMjj8UiSmpubc7U1AAByhqACABbjcrnS92va29vHhJVU16/UfZuGhoY87RQAgOyh6xcAWNBC71wGAMBcEVQAwKKCwaBaWloUCATGvHbttdfq5z//uTZt2pT7jQEAkAMEFQCwMNM09dhjj6mzs1NXXXVVunPZb3/7W23YsEF1dXX53iIAAFlBUAEAizt48KAqKipkGEb6WSAQ0PDwsBobG/O3MQAAsojL9ABgcTabTdFoNONZJBKRzWbL044AAMg+KioAYHHJZFJer1crVqzQJ598op/+9KcaHR3VzTffrK9//etyuVyWHmAJAMBsEFQAYAF4/fXXdffdd6uvr2/Ma4ZhaP/+/XT/AgAUFIIKAFjcpa2KHQ6HWltbVVlZqf7+floVAwAKFkEFACzMNE1t3rxZgUBAbrdb+/btU0lJSfr11PBHr9crwzDU29vLMTAAQEEgqACAhfn9fm3ZskUOh0ORSCQjpKQkEglVVVUpFovJ7/fL5XLlYacAAMwvun4BgIV1dXVJklpbW8cNKZJUUlKitrY2SZLP58vV1gAAyCqCCgBY2MjIiCSpsrJy0nVOpzNjPQAACx1BBQAsrLy8XJLU398/6bqBgYGM9QAALHQEFQCwsKamJkmSx+NRIpEYd00ikZDH45EkNTc352prAABkFUEFACzM5XLJMAzFYjG1t7ePCSuprl+xWEyGYaihoSFPOwUAYH7R9QsALO7SOSp2u11tbW1yOp0aGBhgjgoAoGARVABgAQgGg2ppaVEgEBjzGpPpAQCFiKACAAuEaZrq6emRz+fTyMiIysvL1dzcrIaGBoY8AgAKDkEFAAAAgOVwmR4AAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5RBUAAAAAFgOQQUAAACA5fwfNeP5rkv3M6gAAAAASUVORK5CYII=", + "text/plain": [ + "
" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_network(network);" + ] + }, + { + "cell_type": "markdown", + "id": "5d3030f5", + "metadata": {}, + "source": [ + "Notice that in the visualisation above, the large, heavy circles are the *nodes* (junctions and boundaries), while the small ones strung along each reach are its *break points*. A break point coincident with a node — the first and last gridpoint of a reach — sits beneath that node's circle, which is where it physically is.\n", + "\n", + "#### Looking up a location\n", + "\n", + "`resolve()` takes a location the way the model names it and says what quantities it carries. A node is looked up by its name:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d9d23a8b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.131829Z", + "iopub.status.busy": "2026-10-01T14:06:43.131643Z", + "iopub.status.idle": "2026-10-01T14:06:43.136340Z", + "shell.execute_reply": "2026-10-01T14:06:43.135482Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "5905e267", - "metadata": {}, - "source": [ - "To evaluate the model we need observations at network nodes. These are represented by `NodeObservation`, which requires an integer node ID obtained via `Network.find()`.\n", - "\n", - "In this example we create synthetic sensor observations by extracting data from the network dataset and adding random noise to simulate real-world measurement error and timing jitter:" + "data": { + "text/plain": [ + "NetworkLocation(address='98', quantities=('WaterLevel',), graph_node=478)" ] - }, + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network.resolve(\"98\")" + ] + }, + { + "cell_type": "markdown", + "id": "ae495c5d", + "metadata": {}, + "source": [ + "A break point is looked up by its reach and its position along it, measured from the reach's start node:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "30c88717", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.138713Z", + "iopub.status.busy": "2026-10-01T14:06:43.138520Z", + "iopub.status.idle": "2026-10-01T14:06:43.142553Z", + "shell.execute_reply": "2026-10-01T14:06:43.141826Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 14, - "id": "817e1800", - "metadata": {}, - "outputs": [], - "source": [ - "ds = network.to_dataset()\n", - "\n", - "# Script to generate dummy sensor data\n", - "sensor_1 = ds[\"WaterLevel\"].sel(node=30).to_pandas().rename(\"water_level@sens1\")\n", - "sensor_2 = ds[\"WaterLevel\"].sel(node=54).to_pandas().rename(\"water_level@sens2\")\n", - "sensor_3 = ds[\"WaterLevel\"].sel(node=71).to_pandas().rename(\"water_level@sens3\")\n", - "\n", - "perfect_sensors = [sensor_1, sensor_2, sensor_3]\n", - "real_sensors = []\n", - "\n", - "for n, sensor in enumerate(perfect_sensors, start=1):\n", - " sensor += np.random.normal(0, 0.1, len(sensor))\n", - " sensor.index = [sensor.index[i] + pd.Timedelta(s, unit=\"s\") for i, s in enumerate(np.random.uniform(-10, 10, len(sensor)))]\n", - " sensor.sort_index(inplace=True)\n", - " if n == 2:\n", - " sensor = sensor.iloc[30:]\n", - " if n == 3:\n", - " sensor = pd.concat([sensor.iloc[:50], sensor.iloc[70:]])\n", - "\n", - " real_sensors.append(sensor)\n", - " sensor.to_csv(f\"../tests/testdata/network_sensor_{n}.csv\")\n", - "\n", - "sensor_1 = real_sensors[0]\n", - "sensor_2 = real_sensors[1]\n", - "sensor_3 = real_sensors[2]" + "data": { + "text/plain": [ + "NetworkLocation(address=('44l1', 44.8410077843281), quantities=('Discharge',), graph_node=240)" ] - }, + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network.resolve((\"44l1\", 44.841))" + ] + }, + { + "cell_type": "markdown", + "id": "41ca197f", + "metadata": {}, + "source": [ + "## Integration with `modelskill`\n", + "\n", + "Wrap a `Network` in a `NetworkModelResult` to make it compatible with the standard `modelskill` comparison workflow. The `item` argument selects which quantity to use when more than one is available in the network. Nothing is read yet: each observation's location is read when it is matched." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "edec2e5a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.144472Z", + "iopub.status.busy": "2026-10-01T14:06:43.144254Z", + "iopub.status.idle": "2026-10-01T14:06:43.149141Z", + "shell.execute_reply": "2026-10-01T14:06:43.148348Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 15, - "id": "66d1b420", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
nbiasrmseurmsemaeccsir2
observation
water_level@sens2800.0072410.0972850.0970150.0777890.8507820.0005010.721882
\n", - "
" - ], - "text/plain": [ - " n bias rmse urmse mae cc \\\n", - "observation \n", - "water_level@sens2 80 0.007241 0.097285 0.097015 0.077789 0.850782 \n", - "\n", - " si r2 \n", - "observation \n", - "water_level@sens2 0.000501 0.721882 " - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# The name is taken from the name of the series\n", - "node_id = network.find(reach=\"117l1\", distance=48.7)\n", - "single_obs = ms.NodeObservation(sensor_2, at=node_id)\n", - "cmp = ms.match(single_obs, network_model)\n", - "cmp.skill()" + "data": { + "text/plain": [ + ": WaterLevel" ] - }, + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network_model = ms.NetworkModelResult(network, item=\"WaterLevel\")\n", + "network_model" + ] + }, + { + "cell_type": "markdown", + "id": "5905e267", + "metadata": {}, + "source": [ + "To evaluate the model we need observations at network nodes. These are represented by `NodeObservation`, which is addressed the way the model names the location: a node's name, or a break point as a `(reach, position)` pair.\n", + "\n", + "In this example we create synthetic sensor observations by reading the model's series at the sensor locations and adding random noise to simulate real-world measurement error and timing jitter:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "817e1800", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.151293Z", + "iopub.status.busy": "2026-10-01T14:06:43.151120Z", + "iopub.status.idle": "2026-10-01T14:06:43.282022Z", + "shell.execute_reply": "2026-10-01T14:06:43.281217Z" + } + }, + "outputs": [], + "source": [ + "# Script to generate dummy sensor data. Two sensors sit at named nodes and one\n", + "# at a break point along a reach, which is the pair of forms an observation takes.\n", + "sensor_locations = [\"98\", (\"117l1\", 48.7), \"101\"]\n", + "\n", + "modelled = network.read([(at, \"WaterLevel\") for at in sensor_locations])\n", + "\n", + "sensor_1, sensor_2, sensor_3 = (\n", + " modelled[(at, \"WaterLevel\")].rename(f\"water_level@sens{n}\")\n", + " for n, at in enumerate(sensor_locations, start=1)\n", + ")\n", + "\n", + "perfect_sensors = [sensor_1, sensor_2, sensor_3]\n", + "real_sensors = []\n", + "\n", + "# Seeded so re-running this cell rewrites the committed CSVs unchanged.\n", + "np.random.seed(42)\n", + "\n", + "for n, sensor in enumerate(perfect_sensors, start=1):\n", + " sensor += np.random.normal(0, 0.1, len(sensor))\n", + " sensor.index = [sensor.index[i] + pd.Timedelta(s, unit=\"s\") for i, s in enumerate(np.random.uniform(-10, 10, len(sensor)))]\n", + " sensor.sort_index(inplace=True)\n", + " if n == 2:\n", + " sensor = sensor.iloc[30:]\n", + " if n == 3:\n", + " sensor = pd.concat([sensor.iloc[:50], sensor.iloc[70:]])\n", + "\n", + " real_sensors.append(sensor)\n", + " sensor.to_csv(f\"../tests/testdata/network_sensor_{n}.csv\")\n", + "\n", + "sensor_1 = real_sensors[0]\n", + "sensor_2 = real_sensors[1]\n", + "sensor_3 = real_sensors[2]\n", + "\n", + "# A fourth sensor measures discharge along reach \"116l1\", which holds discharge\n", + "# at a single break point. It is written after the others, so their draws, and\n", + "# their CSVs, do not change.\n", + "[discharge_point] = network.addresses(reach=\"116l1\", quantity=\"Discharge\")\n", + "sensor_4 = network.read([(discharge_point, \"Discharge\")]).iloc[:, 0].rename(\"discharge@sens4\")\n", + "sensor_4 += np.random.normal(0, 0.05, len(sensor_4))\n", + "sensor_4.index = [sensor_4.index[i] + pd.Timedelta(s, unit=\"s\") for i, s in enumerate(np.random.uniform(-10, 10, len(sensor_4)))]\n", + "sensor_4.sort_index(inplace=True)\n", + "sensor_4.to_csv(\"../tests/testdata/network_sensor_4.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "66d1b420", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.283697Z", + "iopub.status.busy": "2026-10-01T14:06:43.283561Z", + "iopub.status.idle": "2026-10-01T14:06:43.384222Z", + "shell.execute_reply": "2026-10-01T14:06:43.383512Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 16, - "id": "ed1f9094", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
nbiasrmseurmsemaeccsir2
observation
network_sensor_2800.0072410.0972850.0970150.0777890.8507820.0005010.721882
\n", - "
" - ], - "text/plain": [ - " n bias rmse urmse mae cc \\\n", - "observation \n", - "network_sensor_2 80 0.007241 0.097285 0.097015 0.077789 0.850782 \n", - "\n", - " si r2 \n", - "observation \n", - "network_sensor_2 0.000501 0.721882 " - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
nbiasrmseurmsemaeccsir2
observation
water_level@sens2800.0048110.106590.1064820.0847230.8543130.000550.725381
\n", + "
" ], - "source": [ - "# The name is taken from the name of the file\n", - "path_to_sensor2 = \"../tests/testdata/network_sensor_2.csv\"\n", - "single_obs = ms.NodeObservation(path_to_sensor2, at=node_id)\n", - "cmp = ms.match(single_obs, network_model)\n", - "cmp.skill()" + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "water_level@sens2 80 0.004811 0.10659 0.106482 0.084723 0.854313 \n", + "\n", + " si r2 \n", + "observation \n", + "water_level@sens2 0.00055 0.725381 " ] - }, + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The name is taken from the name of the series\n", + "# sensor 2 sits at a break point, addressed by its reach and position along it\n", + "at = (\"117l1\", 48.7)\n", + "single_obs = ms.NodeObservation(sensor_2, at=at)\n", + "cmp = ms.match(single_obs, network_model)\n", + "cmp.skill()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ed1f9094", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.385997Z", + "iopub.status.busy": "2026-10-01T14:06:43.385851Z", + "iopub.status.idle": "2026-10-01T14:06:43.455623Z", + "shell.execute_reply": "2026-10-01T14:06:43.454671Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 17, - "id": "de621fec", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
nbiasrmseurmsemaeccsir2
observation
Sensor 2800.0072410.0972850.0970150.0777890.8507820.0005010.721882
\n", - "
" - ], - "text/plain": [ - " n bias rmse urmse mae cc si \\\n", - "observation \n", - "Sensor 2 80 0.007241 0.097285 0.097015 0.077789 0.850782 0.000501 \n", - "\n", - " r2 \n", - "observation \n", - "Sensor 2 0.721882 " - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
nbiasrmseurmsemaeccsir2
observation
network_sensor_2800.0048110.106590.1064820.0847230.8543130.000550.725381
\n", + "
" ], - "source": [ - "# The name is passed\n", - "single_obs = ms.NodeObservation(path_to_sensor2, at=node_id, name=\"Sensor 2\")\n", - "cmp = ms.match(single_obs, network_model)\n", - "cmp.skill()" - ] - }, - { - "cell_type": "markdown", - "id": "2357349d", - "metadata": {}, - "source": [ - "### Plotting" + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "network_sensor_2 80 0.004811 0.10659 0.106482 0.084723 0.854313 \n", + "\n", + " si r2 \n", + "observation \n", + "network_sensor_2 0.00055 0.725381 " ] - }, + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The name is taken from the name of the file\n", + "path_to_sensor2 = \"../tests/testdata/network_sensor_2.csv\"\n", + "single_obs = ms.NodeObservation(path_to_sensor2, at=at)\n", + "cmp = ms.match(single_obs, network_model)\n", + "cmp.skill()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "de621fec", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.457262Z", + "iopub.status.busy": "2026-10-01T14:06:43.457111Z", + "iopub.status.idle": "2026-10-01T14:06:43.528928Z", + "shell.execute_reply": "2026-10-01T14:06:43.527240Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 18, - "id": "923a1d93", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsEAAAK9CAYAAADFUbHOAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAvQ9JREFUeJzs3Qd4U1X/B/BvWzopbUFahpa9BAUUEFwUlTJUFBeKtICCoFRx/eUV90BBRMXRgmhlOABFBKsiKihliIrSFxFQpogyymoLpenK//mda/qmpSNpxr03+X6eJ8ab5tyc3Cblm5PfPSfAarVaQURERETkRwL17gARERERkbcxBBMRERGR32EIJiIiIiK/wxBMRERERH6HIZiIiIiI/A5DMBERERH5HYZgIiIiIvI7DMFERERE5HcYgomIiIjI7zAEExH5gT59+qgLERFpGIKJSFcffvghAgIC8Mknn5z2sy5duqifffvtt6f9rFmzZrjooouceqy0tDTMmTMHnrBnzx7V12nTpnlk/77ixIkTePLJJ3HOOeegbt26OOOMM9C1a1fce++9+Oeff+CLSktL1evummuuQXx8vHre8vwnTZqEgoICvbtH5LcYgolIV5dccom6XrNmTbnbc3NzsXnzZtSpUwdr164t97O//vpLXWxtjRCCqWZFRUXo3bs3XnzxRVx66aV4+eWX8cgjj+D888/HBx98gD/++MMnD2N+fj5uu+02ZGdn484778T06dNxwQUXqA8DAwcOhNVq1buLRH6pjt4dICL/1rRpU7Rs2fK0EPz999+rcHDTTTed9jPbtrMh2BOKi4vVSB/VbMmSJdi4cSPef/993HrrreV+JiOihYWFpj2M8hqQ/oeFhZ32s5CQEPVBzv6bizvuuAMtWrRQQXjFihXo27evl3tMRBwJJiLdSZiVcHTq1Kmy2yQ0dOrUSY2UrV+/vlzQlJ9J6cHFF1+stmfPno3LL78ccXFxCA0NRceOHTFjxoxyjyGB47fffsOqVatUW7nY18geP34c9913n/q6WvbRpk0bvPDCC+Ue177kQUbzWrdure67ZcsWh5+rxWJRwUf2L23l8SZMmKBut5Gvyi+77LLT2kpfzjzzTNx4443lbpO+yLGSANaoUSOMHTsWx44dc7hPtXncBQsWoFu3bqhXrx6ioqJw7rnn4tVXX612/zt37lTXtt+bPem77Mfetm3b1GM2aNBA/bx79+749NNPy91HRvbldyKviQceeACxsbGq3OC6665TI6/2NmzYgP79+6Nhw4YIDw9XH75uv/32cvc5efIkHnzwwbLXQfv27dXvu+JorTzm3XffrQK9HHu575dfflnp85YQXFnpjvRRbN26tdrjRkSewZFgIjJECH733Xfxww8/lAVT28iZXHJyclRpROfOnct+1qFDB1VPKiTwShCRmkspn8jIyMC4ceNUeEtJSVH3kaB4zz33IDIyEo8++qi6TQKj7evqhIQE/P333ypASr3xunXrMHHiROzfv1+1tSehW0Yux4wZo8KPhDRHRoPlPtJHGcmWtmeffTZ+/fVXvPLKK6oUQEZKxc0334ynnnoKBw4cQOPGjcvaSzupm73lllvKbpP+ShCUr9vHjx+P3bt344033lAfKuQ4BQcHO/x7cPRxv/76awwdOhRXXHGF+qBgC3LyeFLbW5XmzZur63nz5uGxxx5TQbIq8oFFwrKE74cfflgFW6kfHzx4MD7++OOyAGkjv9v69eurDxjyYUV+ZxJSFy5cqH5+6NAh9OvXT4Vk2V9MTIy63+LFi8v2IUFXfj9Sgz5q1ChVq7x8+XI89NBD6rUhvyd7K1euVH2Sx5FgLR+0nCHHWUhbItKBlYhIZ7/99psMs1mfffZZtV1UVGStW7eude7cuWq7UaNG1tTUVPX/ubm51qCgIOsdd9xR1j4/P/+0ffbv39/aqlWrcrd16tTJmpCQcNp95XHl8f74449ytz/88MPqsfbu3au2d+/erfoZFRVlPXToULn72n724osvVvk83333XWtgYKB19erV5W6fOXOmart27Vq1/fvvv6vt119/vdz9xo0bZ42MjCx7vrIfud/7779f7n5ffvnlabfL867sudtz9HHvvfdedQyKi4utzpD27du3V4/RvHlz68iRI63p6enWgwcPnnbfK664wnruuedaCwoKym4rLS21XnTRRda2bduW3TZ79my1v759+6qf29x///3qd3f8+HG1/cknn6j7/fTTT1X2b8mSJeo+kyZNKnf7jTfeaA0ICLDu2LGj7Da5n/wu5bVbW9JnOY7Hjh2r9T6IqPZYDkFEupMRURnVtdX6/ve//1VfS9u+QpZr28lxUitcUlJSrh5Yvtq2kVHjw4cPq5HdXbt2qe2afPTRR+pE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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
nbiasrmseurmsemaeccsir2
observation
Sensor 2800.0048110.106590.1064820.0847230.8543130.000550.725381
\n", + "
" ], - "source": [ - "cmp = ms.match(single_obs, network_model)\n", - "cmp.plot()\n", - "cmp.plot.timeseries();" + "text/plain": [ + " n bias rmse urmse mae cc si \\\n", + "observation \n", + "Sensor 2 80 0.004811 0.10659 0.106482 0.084723 0.854313 0.00055 \n", + "\n", + " r2 \n", + "observation \n", + "Sensor 2 0.725381 " ] - }, + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The name is passed\n", + "single_obs = ms.NodeObservation(path_to_sensor2, at=at, name=\"Sensor 2\")\n", + "cmp = ms.match(single_obs, network_model)\n", + "cmp.skill()" + ] + }, + { + "cell_type": "markdown", + "id": "2357349d", + "metadata": {}, + "source": [ + "### Plotting" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "923a1d93", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:43.532243Z", + "iopub.status.busy": "2026-10-01T14:06:43.531818Z", + "iopub.status.idle": "2026-10-01T14:06:44.146428Z", + "shell.execute_reply": "2026-10-01T14:06:44.145756Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "02e77cf0", - "metadata": {}, - "source": [ - "## Multiple sensors\n", - "\n", - "When you have observations at several nodes you can use `NodeObservation.from_multiple()` to create a list of `NodeObservation` objects. Pass `nodes` as a `dict` mapping each node ID to either a column name/index within a shared `data` source, or to a separate data source entirely:" + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsEAAAK9CAYAAADFUbHOAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAArpBJREFUeJzs3XlYVGX/x/H3gAybgmIgUeC+m1LaZpZWLllZtjz1q9zSdsrK0kfbbNfK1BZcKlNLK1tNn0ozW1AyyxLJ1FLM1BLBVFZhkDm/P05MoqgsM3Nm4PO6Lq86s5z7OwPoh+/c575thmEYiIiIiIjUIQFWFyAiIiIi4m0KwSIiIiJS5ygEi4iIiEidoxAsIiIiInWOQrCIiIiI1DkKwSIiIiJS5ygEi4iIiEidoxAsIiIiInWOQrCIiIiI1DkKwSIidUCvXr3o1auX1WWIiPgMhWARsdS7776LzWbjo48+OuK+Ll26YLPZ+Oqrr464LyEhge7du1dprGnTpjFnzpzqlnpM27Ztw2azMWnSJI+cv7bIz89n/PjxdOrUifDwcBo3bkxiYiJ33303f/31l9XleYTT6WTOnDlcdtllxMfHEx4eTqdOnXjyyScpKiqyujyROkshWEQs1aNHDwBWrlxZ7vbc3FzWr19PvXr1SE1NLXffjh072LFjh+u5leXJECzHV1JSwnnnncdzzz3Hueeey+TJk3nggQc47bTTeOutt/jtt9+sLtEjCgsLufHGG8nOzua2225j6tSpnHHGGYwfP57+/ftjGIbVJYrUSfWsLkBE6ra4uDiaN29+RAhetWoVhmHwn//854j7yo6rGoI94eDBgzidTqvL8AsLFy5k7dq1zJ8/n+uvv77cfUVFRTgcDosqqzmn04nD4SAkJOSI++x2O6mpqeU+ubj55ptp1qwZ48ePZ/ny5fTu3dub5YoI6gSLiA/o0aMHa9eu5cCBA67bUlNT6dixI/379+e7774rFzRTU1Ox2Wycc845AMyePZsLLriAmJgYgoOD6dChA9OnTy83RrNmzfjll1/45ptvsNls2Gy2cnNk9+/fzz333EN8fDzBwcG0atWKZ555pty4h055mDp1Ki1btiQ4OJgNGzZU+rUWFxczfvx4WrVqRXBwMPHx8YwZM4bi4mLXYzp16sT5559/xHOdTicnnXQSV199dbnbpk6dSseOHQkJCaFJkybceuut7Nu3r9I1VWfcd955h65du9KgQQMiIiI45ZRTeOGFF455/oyMDADX1+1QISEhRERElLtt06ZNXH311URFRRESEkK3bt1YtGhRucfMmTMHm81Gamoqo0aNIjo6mvDwcK644gqys7PLPXbNmjX069ePE044gdDQUJo3b87w4cPLPaagoID77rvP9X3Qtm1bJk2adES31mazceeddzJ//nw6duxIcHAwS5YsqfB12+32CqfuXHHFFQBs3LixwueJiGepEywiluvRowdvvvkmq1evdgXTss5Z9+7dycnJYf369XTu3Nl1X7t27WjcuDEA06dPp2PHjlx22WXUq1ePxYsXc8cdd+B0OklKSgJg6tSp3HXXXdSvX58HH3wQgCZNmgDmx9U9e/bkzz//5NZbbyUhIYFvv/2WcePGsWvXLqZOnVqu3tmzZ1NUVMQtt9xCcHAwUVFRleoGO51OLrvsMlauXMktt9xC+/bt+fnnn5kyZQq//fYbCxcuBODaa6/l0UcfJTMzk9jYWNfzV65cyV9//cX//d//uW679dZbmTNnDjfeeCMjR47k999/5+WXX2bt2rWkpqYSFBRU6a9DZcddtmwZ1113HRdeeCHPPPMMYAa51NRU7r777qOev2nTpgC88cYbPPTQQ9hstqM+9pdffuGcc87hpJNOYuzYsYSHh/Puu+8ycOBAPvjgA1eALHPXXXfRqFEjxo8fz7Zt25g6dSp33nknCxYsACArK4u+ffsSHR3N2LFjadiwIdu2bePDDz90ncMwDC677DK++uorRowYQWJiIkuXLmX06NH8+eefTJkypdyYX375Je+++y533nknJ5xwAs2aNavEu/yvzMxMAE444YQqPU9E3MQQEbHYL7/8YgDGE088YRiGYZSUlBjh4eHG3LlzDcMwjCZNmhjJycmGYRhGbm6uERgYaNx8882u5xcWFh5xzn79+hktWrQod1vHjh2Nnj17HvHYJ554wggPDzd+++23crePHTvWCAwMNLZv324YhmH8/vvvBmBEREQYWVlZ5R5bdt9zzz131Nf55ptvGgEBAcaKFSvK3T5jxgwDMFJTUw3DMIxff/3VAIyXXnqp3OPuuOMOo379+q7Xu2LFCgMw5s+fX+5xS5YsOeL2nj17VvjaD1XZce+++24jIiLCOHjw4DHPd7jCwkKjbdu2BmA0bdrUGDZsmDFr1ixj9+7dRzz2wgsvNE455RSjqKjIdZvT6TS6d+9utG7d2nXb7NmzDcDo3bu34XQ6Xbffe++9RmBgoLF//37DMAzjo48+MgDjhx9+OGp9CxcuNADjySefLHf71VdfbdhsNmPLli2u2wAjICDA+OWXX6r0Hhyqd+/eRkREhLFv375qn0NEqk/TIUTEcu3bt6dx48auub7r1q2joKDA9RFy9+7dXRfHrVq1itLS0nLzgUNDQ13/n5OTw549e+jZsydbt24lJyfnuOO/9957nHvuuTRq1Ig9e/a4/vTu3ZvS0lJSUlLKPf6qq64iOjq6yq/zvffeo3379rRr167cOBdccAGAaxWMNm3akJiY6OpiApSWlvL+++8zYMAA1+t97733iIyMpE+fPuXO17VrV+rXr1/hqhrHUtlxGzZsSEFBAcuWLavS+UNDQ1m9ejWjR48GzKkMI0aM4MQTT+Suu+5yTQnZu3cvX375Jddccw15eXmu1/X333/Tr18/Nm/ezJ9//lnu3Lfccku5zvK5555LaWkpf/zxh6tmgP/973+UlJRUWN+nn35KYGAgI0eOLHf7fffdh2EYfPbZZ+Vu79mzJx06dKjSe1Dm6aef5osvvmDixImu2kTEuxSCRcRyNpuN7t27u+b+pqamEhMTQ6tWrYDyIbjsv4eG4NTUVHr37k14eDgNGzYkOjqaBx54AKBSIXjz5s0sWbKE6Ojocn/KLlbKysoq9/jmzZtX63Vu3ryZX3755Yhx2rRpc8Q41157Lampqa6w9/XXX5OVlcW1115b7nw5OTnExMQccc78/Pwj6q6Myox7xx130KZNG/r378/JJ5/M8OHDjzof9nCRkZE8++yzbNu2jW3btjFr1izatm3Lyy+/zBNPPAHAli1bMAyDhx9++IjXNX78+CPeKzCXzDtUo0aNAFxzo3v27MlVV13FY489xgknnMDll1/O7Nmzy83F/uOPP4iLi6NBgwblztW+fXvX/Yeq7vfBggULeOihhxgxYgS33357tc4hIjWnOcEi4hN69OjB4sWL+fnnn4+4kr579+6ueZkrV64kLi6OFi1aAObFVhdeeCHt2rVj8uTJxMfHY7fb+fTTT5kyZUql5+r26dOHMWPGVHh/WUgtc2jnuSqcTiennHIKkydPrvD++Ph41/9fe+21jBs3jvfee4977rmHd999l8jISC666KJy54uJiWH+/PkVnq863erKjBsTE0NaWhpLly7ls88+47PPPmP27NkMGTKEuXPnVnqspk2bMnz4cK644gpatGjB/PnzefLJJ11fs/vvv59+/fpV+NyyX5DKBAYGVvg4458L2mw2G++//z7fffcdixcvZunSpQwfPpznn3+e7777jvr161e67jLV+T5YtmwZQ4YM4ZJLLmHGjBlVfr6IuI9CsIj4hEPXC05NTeWee+5x3de1a1eCg4P5+uuvWb16NRdffLHrvsWLF1NcXMyiRYvKdQMrmgpwtAuxWrZsSX5+vseXqWrZsiXr1q3jwgsvPOZFYWB2Gc844wwWLFjAnXfeyYcffsjAgQMJDg4ud74vvviCc845p9rBvDrjgrniwYABAxgwYABOp5M77riDmTNn8vDDDx8RUI+nUaNGtGzZkvXr1wO4fsEJCgpy+9fkrLPO4qyzzuKpp57irbfe4oYbbuCdd97hpptuomnTpnzxxRfk5eWV6wZv2rQJ+PfCvupavXo1V1xxBd26dePdd9+lXj39EyxiJU2HEBGf0K1bN0JCQpg/fz5//vlnuU5wcHAwp512GsnJyRQUFJSbClHWATQOWcIqJyeH2bNnHzFGeHg4+/fvP+L2a665hlWrVrF06dIj7tu/fz8HDx6syUsrN86ff/7Jq6++esR9Bw4coKCgoNxt1157Ld999x2vv/46e/bsKTcloex8paWlrmkEhzp48GCFr7Uyjjfu33//Xe44ICDAtXLHodMLDrdu3Tr27NlzxO1//PEHGzZsoG3btoDZae7VqxczZ85k165dRzz+8KXPKmPfvn1HLHOWmJhYruaLL76Y0tJSXn755XKPmzJlCjabjf79+1d53DIbN27kkksuoVmzZvzvf/9z2y8tIlJ9+jVURHyC3W7n9NNPZ8WKFQQHB9O1a9dy93fv3p3nn38eKD8fuG/fvq6u5K233kp+fj6vvvoqMTExRwSorl27Mn36dJ588klatWpFTEwMF1xwAaNHj2bRokVceumlDBs2jK5du1JQUMDPP//M+++/z7Zt2yq9jNXy5csr3Ap34MCBDB48mHfffZfbbruNr776inPOOYfS0lI2bdrEu+++y9KlS+nWrZvrOddccw33338/999/P1FRUUd0RXv27Mmtt97KhAkTSEtLo2/fvgQFBbF582be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DoMH4+oAL14cTHCw++o49O/+49GETBERERHxuEMvgrOyA1xGnWAvCwszu6+eUDaftiqdt7Awz9QiIiIiUubwVSCmTbM2AINCsNfZbO6ZflCRsmvKfOATcBERERGg4mXQ3HERXE1pOoR43NGWSBMREZHazcp1gI9HnWCpkZdffpn9+/fz119/AfC///2PnTt3EhgYyF133UVkZCQfffQRN954I7Nnz2bYsGGu56akpJCSkgKYy7UUFBTw5JNPAnDeeedx3nnnef31iIiIiHv4cgAGhWCpoUmTJvHHH3+4jhcuXMjChQsBGDRoEJGRkUd97pdffsljjz1W7raHH34YgPHjxysEi4iI+ClfD8CgECw1tG3btnLHFV2cN2zYsHId4DKPPvoojz76qAerExEREW/zhwAMmhMsIiIiIm7iLwEYFIJFRERExA38KQCDQrCIiIiI1JC/BWBQCBYRERGRGvDHAAwKwSIiIiJSTf4agEEhWERERESqwZ8DMCgEi4iIiEgV+XsABoVgEREREamC2hCAQSFYRERERCqptgRgUAgWERERkUqoTQEYFIJFRERE5DhqWwAGhWAREREROYbaGIBBIVhEREREjqK2BmBQCBY3+OWXXxg0aBAnnXQS9evXp2nTpgwaNIgNGzbU6FzBwcHExcVV+1wiIiJSfbU5AINCsNTQhx9+yGmnncby5cu58cYbeemllxg2bBhffvklp512Gh9//HG1zzVt2jRGjBhRrXOJiIhI9dX2AAxQz+oCxH9lZGQwePBgWrRoQUpKCtHR0ZSUlAAwatQozj33XAYNGkR6ejrNmzev8rnK3H333VU6l4iIiFRfXQjAoE6wXzIMg4KCAgAKCgowDMOSOp577jkKCwt55ZVXyoVWgBNOOIGZM2eSn5/Pc88959VziYiISPXUlQAMCsF+xzAMXln6OZfMf4vvflrLJfPf4pWln1sShBcvXkyzZs0499xzK7z/vPPOo1mzZixevNir5xIREZGqq0sBGBSC/U5hYSFvb9/ON50T6Z+xlW86J/L29u0UFhZ6tY6cnBz++usvunTpcszHde7cmZ07d5KXl+eVc4mIiEjV1bUADArBfic8PJyJ3brRcMd29rdqTcMd25nYrRvh4eFeraMsiDZo0OCYjyu7/1jB1Z3nEhERkaqpiwEYFIL9TkFBAWPXrGF/fAINt2xmf3wCY9escc0R9pbKBtK8vDxsNhsnnHACe/fuJTMz0/UnJyen2ucSERGRmqurARgUgv1OWFgY1yUk0DM9jc9atqBnehrXJSQQFhbm1ToiIyOJi4sjPT39mI9LT0/n5JNPxm63c+WVV3LiiSe6/tx9993VPpeIiIjUTF0OwKAl0vyOzWbjln59GVRYSHh4OJ+0bUNYWBg2m83rtQwYMICZM2eycuVKevToccT9K1asYNu2bYwaNQqA559/nn379rnuj4uLq/a5REREpPpSUoIYPLjuBmAAm2HV+lp+Ljc3l8jISLZs2UKDBg3Izs4GcC3vVVJSwr59+2jatCkhISFeqengwYMA1Kvnnd9ttmzZQrdu3WjatClffvkljRs3dtWQm5vL+eefz59//skPP/xw3LV9KzpXmb1791bpXN5+H3xt/LpcQ1FREX/88QeNGjUiKCjoiJ9LK1hdg9XjqwbfqcHq8VWD79SweHEhSUkJFBcH0KdPMbNm5Xg9AHvqPcjLy6NVq1bk5OQQERFxzMeqE1xFycnJJCcnU1paanUplmvVqhWvv/46gwcP5rTTTmPYsGEkJCTwxx9/8MYbb7Bv3z7mzZtXqc0tKjpX8+bN2bZtG3PmzKnSuURERKRiKSlBlgdgX6FOcDWVdYLLftPIysoCICYmBjA7Ur///jvNmzf3Wie4bLe2oKAgr4xXZv369UyYMIEvv/ySrKwsnE4nISEh/Pjjj3To0MHr57LqffCV8etyDYf/3B3+c2kFq2uwenzV4Ds1WD2+arC+hkPnAPfpU8zixcGWBWBPvQeH57Nj0YVxUmOdOnVi/vz57Nq1i6KiImbNmkVxcTETJ06s0blKS0uZO3dutc8lIiIipsMDcF3uAJfRdAhxu8GDB5Odnc3YsWM5+eSTefrpp6t9riFDhrBr1y63nEtERKQuOnwViGnTFIBBIVg85L///S///e9/fe5cIiIidUlFy6D9s0x/nafpECIiIiK1UF1fB/h4FIJFREREahkF4ONTCBYRERGpRRSAK0chWERERKSWUACuPIVgERERkVpAAbhqFIJFRERE/JwCcNUpBIuIiIj4MQXg6lEIFhEREfFTCsDVpxAsIiIi4ocUgGtGIVg8Ytu2bdhsNubMmWN1KSIiIrWOAnDNKQRLtcyZMwebzXbEH7vdzgMPPFDhcz799FMeffRR7xZaDU6nk2effZbmzZsTEhJC586defvtt6t0juXLl3PBBRcQGRlJgwYN6Nq1KwsWLCj3mKKiIiZMmECHDh0ICwvjpJNO4j//+Q+//PKLO19OpTidTqZPn05iYiKhoaE0btyYCy64gHXr1rkes2nTJsaMGUNiYiINGjTgxBNP5JJLLmHNmjVVGuunn37isssuIyoqirCwMDp16sSLL754RD0zZswgMTGR+vXr06RJE/r378+3337rltcrIuLPFIDdo57VBYh/e/zxx2nevLnr+ODBg3Ts2JGmTZty4MABgoKCXPd9+umnJCcn+3wQfvDBB5k4cSI333wzp59+Oh9//DHXX389NpuN//u//zvu8+fOncstt9xCnz59ePrppwkMDOTXX39lx44d5R53ww03sGjRIm6++WZOO+00/vrrL5KTkzn77LP5+eefadq0qade4hGGDx/O/PnzGTJkCHfeeScFBQWsXbuWrKws12Nee+01Zs2axVVXXcUdd9xBTk4OM2fO5KyzzmLJkiX07t37uON8/vnnDBgwgFNPPZWHH36Y+vXrk5GRwc6dO8s9bvTo0UyePJlBgwZxxx13sH//fmbOnEnPnj1JTU3ljDPOcPt7ICLiDxSA3ciQasnJyTEAIycnxzAMw9i9e7exe/du1/0HDhwwNmzYYBw4cMBrNTkcDsPhcHhlrNmzZxuA8cMPP1S6hqSkJMMb33I1eR927txpBAUFGUlJSa7bnE6nce655xonn3yycfDgwWM+//fffzdCQ0ONO++887jjAMb9999f7vYvv/zSAIzJkydXq/4yZe8BYMyePfuYj12wYIEBGB9++OExH7dmzRojLy+v3G179uwxoqOjjXPOOeeoNZTJyckxmjRpYlxxxRVGaWnpUccpKSkxQkNDjauvvrrc7Vu3bjUAY+TIkUd97uE/d4f/XFrB6hqsHl81+E4NVo+vGmpewxdfGEZIiGGAYVx6qWEUFXl3fHeqaQ1Op9PIz883DMMw8vPzDafTaRjGkfnsWDQdQjzi8DnBw4YNIzk5GaDc9Ikyu3btYtOmTZSUlBz1nIZh0KxZMy6//PIj7isqKiIyMpJbb721RnV//PHHlJSUcMcdd7hus9ls3H777ezcuZNVq1Yd8/kzZsygtLSU8ePHA5Cfn49hGEc8Li8vD4AmTZqUu/3EE08EIDQ0FICsrCyio6Pp1atXufNs2bKF8PBwrr322mq8yvImT57MGWecwRVXXIHT6aSgoKDCx3Xt2pX69euXu61x48ace+65bNy48bjjvPXWW+zevZunnnqKgIAACgoKcDqdRzyupKSEAwcOHPHexMTEEBAQ4HpvRETqEl/oADudTtLS0liyZAlpaWkV/h3uDYZh8MrSz7lk/lt899NaLpn/Fq8s/bzCf2+PRSFYaiQnJ4c9e/aU+1ORW2+9lT59+gDw5ptvuv6UGTduHO3bt+fPP/886lg2m41Bgwbx2WefsXfv3nL3LV68mNzcXAYNGuS67fC6jvanuLjY9Zy1a9cSHh5O+/bty52/7OP3tWvXHvP9+OKLL2jbti2fffYZJ598Mg0aNKBx48Y8/PDD5f6yaNmyJSeffDLPP/88ixcvZufOnXz//ffcdtttNG/e3DXtIiYmhunTp/PNN9/w0ksvAeZfQsOGDaNBgwZMmzbtmPUcT25uLt9//z2nn346DzzwAJGRkdSvX58WLVrw7rvvVuocmZmZnHDCCcd93BdffEFERAR//vknbdu2pX79+kRERHD77bdTVFTkelxoaChnnnkmc+bMYf78+Wzfvp309HSGDRtGo0aNuOWWW6r9ekVE/JEvBGCA9PR0Fi9eTGpqKosXLyY9Pd37RQCFhYW8vX0733ROpH/GVr7pnMjb27dTWFhYpfNoTrC3GQZU8YtUaWVd1EPm4R5XWBgc0pGtqormgTocjiNuO/vss2nTpg3Lli0rF1SrasiQITz11FO8++673Hbbba7b582bR7NmzejRowcHDx4EIDo6ulLnnD17NsOGDQPMjnSTJk3Kdanh3w7tX3/9dcxzbd68mcDAQG6++WbGjBlDly5d+PDDD3nyySc5ePAgEyZMACAoKIgPPviA66+/nssuu8z1/K5du/Ltt9/SsGFD121XX3011113HePGjaN///58/PHHpKamsnDhQho3blyp13g0GRkZGIbBO++8Q7169Xj22WeJjIzkhRde4P/+7/+IiIjgoosuOurzV6xYwapVq3jooYeOO9bmzZs5ePAgl19+OSNGjGDChAl8/fXXvPTSS+zfv7/cxYfz5s3j2muvLfe90qJFC1JTU2nRokWNXrOIiD/xlQAMZtPD4XDQsmVLMjIyyMzMtKSO8PBwJnbrRv+Mrexv1ZqGWzYzsVs3wsPDyc3NrfR5FIK9rbAQDvtI2V2qEH3/lZ8P4eHVHjM5OZk2bdq4jssCaFXNmTOnUsuptWnThjPPPJP58+e7QvDevXv57LPPGDNmTLnwumzZskqN3bFjR9f/HzhwgOAK/nYJCQlx3X8s+fn5OJ1OnnrqKdcqGVdddRV79+7lhRde4IEHHqBBgwYANGrUiMTERP7zn/9w1llnsWXLFiZMmMB//vMfli1b5hoT4OWXX+brr7/m6quv5rfffmPw4MFHTAspLCx0/RZ86LSS/Pz8ch36wMBAGjVq5LoP4O+//+a7777jzDPPBOCyyy6jefPmPPnkk0cNwVlZWVx//fU0b96cMWPGHPN9KRursLCQ2267zbUaxJVXXonD4WDmzJk8/vjjtG7dGoAGDRrQsWNHzj77bC688EIyMzOZOHEiAwcOZMWKFZXqPIuI+DtfCsAAsbGx2O12MjIysNvtxMbGWlJHQUEBY9esYX/nRBpu2cz++ATGrlnDJ23bHP/Jh1AIlho544wz6Natm+v4WHN63aVsBYM//viDpk2b8t5771FSUsLgwYPLPa4yqxUcLjQ0tNz0iDJlH9cfbz5qaGgoBQUFR8zVve6661iyZAlr167lvPPOIycnh3PPPZfRo0dz3333uR7XrVs3evXqxezZs7n99ttdt0dFRfHiiy/yn//8hyZNmhyxpBjAs88+y2OPPXbE7XfddRd33XWX67hp06Zs27at3Otp3ry5KwAD1K9fnwEDBjBv3jwOHjxIvXrl/6ooKCjg0ksvJS8vj5UrVx4xV/ho703Ze3Go66+/npkzZ7Jq1Spat27NwYMH6d27N7169XJNAQHz69mxY0eee+45nnnmmeOOJyLiz3wtAAN07twZMDvCsbGxrmNvCwsL47qEBEhPY2K3boxds4brEhIICwtzXXNTGQrB3hYWZnZfPaAsgAZVdTqEn/m///s/7r33XubPn88DDzzAvHnz6NatG23bti33uMp+TBMZGekKaCeeeCJfffUVhmEcceEeQFxc3DHPFRcXx+bNmyu8qAtg3759AHzwwQfs3r273FQIgJ49exIREUFqamq5EAywdOlS1zl27txZbsoEmL8c9OjRA/i3I9+/f39Gjx5N3759XY87NMiXvZ7D6y2ruaSkhIKCAiIjI123OxwOrrzyStLT01m6dCmdOnU65nty6Fi//PLLcd+blJQU1q9fz+TJk8s9rnXr1rRv357U1NRKjSci4q98MQADBAQEkJiYaHUZ2Gw2bunXl0GFhYSHh/NJ2zaEhYUdMZXxeBSCvc1mq9H0g2OqzpxgL6rqN+fRREVFcckllzB//nxuuOEGUlNTmTp16hGPK5vHezyHzglOTEzktddeY+PGjXTo0MH1mNWrV7vuP5auXbuyefNm18VfZcrmEpfNU969ezcApaWl5Z5vGAalpaVHTCtZsmQJr732GmPGjGH+/PkMHTqU1atXl+vQtmjRwjVf9tCOfIcOHY7aFY+LiyM2NrbCCxL/+usvQkJCXNM3wLwob8iQISxfvpx3332Xnj17HvP9OFTXrl1ZtmxZtd+bstdV3Sk3IiL+wFcDsK+x2WyE/5OnwquZq7Q6hHhN2Tfp/v37j7ivMkukHWrw4MFs2LCB0aNHExgYWOEmFsuWLavUn379+rmec/nllxMUFFRu1QXDMJgxYwYnnXQS3bt3P2bNZdMgZs+e7brN6XQye/ZsoqKi6Nq1K4BrHvU777xTruZFixZRUFDAqaee6rpt//793HTTTZxxxhk8/fTTvPbaa/z00088/fTTlXqvjufaa69lx44d5eZQ79mzh48//pgLLriAgIB//5q46667WLBgAdOmTePKK6886jn37NnDpk2byl2pe8011wAwa9asco997bXXqFevHr169QKO/t789NNP/Prrr+XeGxGR2kQB2LvUCRavKQuAI0eOpF+/fuXC67hx45g7dy6///47zZo1O+65LrnkEho3bsx7771H//79XR+pH6o6c4JPPvlk7rnnHp577jlKSko4/fTTWbhwIStWrGD+/PkEBga6HltRzZdffjkXXHABzz77LPv27aNLly4sXLiQlStXMnPmTNdFdwMGDKBjx448/vjj/PHHH64L415++WVOPPFERowY4Rrn7rvv5u+//+aLL74gMDCQiy66iJtuuoknn3ySyy+/nC5dulT5dR5q3LhxvPvuu1x11VWMGjWKyMhIZsyYQUlJSbmgPXXqVKZNm8bZZ59NWFgY8+bNK3eeK664wvWLzssvv8xjjz3GsmXLXF+HU089leHDh/P6669z8OBBevbsyddff817773HuHHjXFMzunbtSp8+fZg7dy65ubn07duXXbt28dJLLxEaGso999xTo9crIuKLamsAdjqdpKenl5tHfGhzxUoK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2cz++AReT09jQu/e5OTklAuhFXFndxz+/aXk8O91X75A0Zfo74q6RSG4hrKzsykqKiLbB/aEVw2+UYPV46uGI2tYv349S5cupaSkhKCgIPbu3UunTp28WkN1OJ1ONmzYQFZWFjExMXTo0KHK/yB76+tgt9txOBykp6cTFBREcHAwcXFxxMXFkZ2dzd9//01sbCznnHOO6/XExsaSlZVV7fFyc3PJzs6mQYMGOBwONm7cyLLNW1i4O4sH27flqY2/MrBJDM24gMGDG1JcbKNPn2KmTcshJ+fIcwZ9+y0Rd99N4PbtABQOH07+Qw9BeLi5LFoFr7VevXpcEBpK/TU/mGOu+YFLmsTQokULbDYbAHv27AEq/lps2rSJffv2uebrbtq0yRVWq6vsfT907KON722H1uCO7++a1nA4b/1dYfXXwurxPVlDXl5epR+rEFxFycnJJCcnU1paanUpIlJJWVlZlJSUuIJGdYOXt23YsKHcP8iA18J7VZWt/nBooDlcQECA2+rv0KED5557LitWrKBevXpERUURERHBwt//4Ntup3P9H9vI6XY6+5evIeMJO8XFAfTsmcesWQeO7AAXFlJ/wgRCX30Vm2FQevLJ5E6dSsm551bqtbZv356ioiLCw8OZ16KFq/tcGTExMQQFBbFt2zYKCwvZuXMn69ev91ogtJIvfn/7698VUj0KwVWUlJREUlISubm5REZGEh0dTUREhOv+mJgYC6tTDb5Ug9Xjq4Z/tWvXjs2bN5OdnU2jRo1o166d1+uqznhlH5G3b9+ejIwMHA5Htev2xus93vQGd9dw44030rVr13IfXTdOW0f/jK3sb9OW+hs3s/mZsygprk+fPsXMmnWA+PiYch95t8jKovVTT2H77TfzpDfdRODzz9PokL/XK1KTqRyHvg+9evUiKirKNad6//79pKamenQZM1/4mYyJiXHr93d1azict/+usPprYfX4nqghJCSk0o9VCBaRWq+ieZL+oKKLnPyZu+dbls11LVNQUMDYNWvY3zmR+hs3k98iAS5dw0X72vDKKwWuDnB6ejqffvQRPb74glarVmEzDIiLg9deg/79a/gqq/caDp9TXReWMfPF729//btCqkchWERqvcPDkr+obf8gu/sisMOFhYVxXUIC+75JY+NT3eDSNZySl8BHH4WRm1vgelzhihWMmD6dJv/M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//VfevDBB3XVVVfJ5XLp1VdfvdyvHwB8ghIMAA3w7bffKj8/X2PHjlVUVJTXZ/Hx8crIyNDChQtljPG8/8ILL6h79+7asmWLnnrqKWVnZ2vlypWS3AVy5syZ+u///m8VFxdr6dKl6tatm+dnH330UX3yySd66623tG3bNv385z/XgAEDVFxc7Nnn1KlTmj59uv70pz/ps88+U0ZGhlq1aqW//e1vnn1qamq0cOFCZWRkSJIqKyt18803691339WOHTs0evRoZWZm6tNPP5UkzZ49W71799Yvf/lLlZSUqKSkRG3btj3v+9i0aZOGDBmif//3f9f27dv17LPPavLkyZo3b57XfjNmzNAtt9yiLVu2aOzYsfqP//gP7dq16zL/FADABwwAoN7Wr19vJJklS5bU+fmLL75oJJkjR44YY4xJSkoyAwYM8Npn6NChJi0tzRhjzIwZM0ynTp3M6dOnzzvWF198YUJDQ82hQ4e83v/pT39qJk2aZIwxJi8vz0gyW7du9donOzvb3HXXXZ7t/Px8ExERYY4dO3bBcxs4cKAZP368Z7tv374mOzvba5+CggIjyXOc4cOHm9TUVK99JkyYYLp06eLZTkpKMiNGjPBs19bWmri4ODNnzpwLZqmPi/051JUdAM7GSjAAXAZz1krvpZx7k1bv3r1VVFQkSfr5z3+u7777Ttdee61++ctfasmSJfrnP/8pSdq+fbtqamrUqVMnRUdHe15r167V559/7jleeHi4brjhBq8xMjIytGbNGn311VeSpPnz52vgwIGeJynU1NRoypQp6tatm2JjYxUdHa38/Hx9+eWXDfoeioqKdPvtt3u9d/vtt6u4uFg1NTWe987O53A4FB8fr6NHjzZoLADwJUowADTAddddJ4fD4Smx5yoqKtLVV18tp9NZr+O1bdtWu3btUm5urqKiojR27Fj16dNH1dXVOnnypEJDQ7Vp0yZt3brV8yoqKtLs2bM9x4iKijrvRrWePXuqQ4cOeuutt/Tdd99pyZIlnkshJPclGrNnz9bEiRNVUFCgrVu3qn///jp9+vRlfCuXFhYW5rXtcDj8egMfAFxKM6sDAEBj0rp1a6Wmpio3N1ePP/6413XBhw8f1vz58zVy5EivUrp+/XqvY6xfv14/+tGPPNtRUVEaNGiQBg0apKysLCUnJ2v79u266aabVFNTo6NHj+onP/lJg7NmZGRo/vz5SkxMVEhIiAYOHOj5bN26dRo8eLBGjBghSaqtrdXu3bvVpUsXzz7h4eFeq7l1+dGPfqR169Z5vbdu3Tp16tRJoaGhDc4MAIHCSjAANNDLL7+sqqoq9e/fX4WFhTpw4IBWrFih1NRUtWnTRr/73e+89l+3bp1+//vfa/fu3crJydGiRYuUnZ0tyf10h7lz52rHjh3au3ev3nzzTUVFRSkpKUmdOnVSRkaGRo4cqcWLF2vfvn369NNPNXXqVL377ruXzJmRkaHNmzfrd7/7ne6//35FRER4PuvYsaNWrlypjz/+WEVFRRozZoyOHDni9fPt2rXThg0btH//fn399dd1rtyOHz9eq1ev1pQpU7R79269/vrrevnll/Xkk09ezlcLAAFDCQaABurYsaM2btyoa6+9VkOGDFGHDh00evRo3Xnnnfrkk08UGxvrtf/48eO1ceNG3XTTTXr++ef14osvqn///pLcz7l97bXXdPvtt+uGG27QqlWrtHz5crVu3VqS+5dBjBw5UuPHj1fnzp2Vnp6uv//973K5XJfMed111+nWW2/Vtm3bvC6FkKSnn35aPXr0UP/+/XXHHXcoPj5e6enpXvs8+eSTCg0NVZcuXeR0Ouu8XrhHjx76y1/+orfeekvXX3+9fvvb3+q5557TqFGjGvCNun8xR0N/BgCuhMM05O4OAAD8ICkpSf/5n//ZoCLscDi0ZMmS88q75C7VN954Y5P6ldUAfIuVYACApT777DPFxMRo5MiRDf7ZYcOGKTEx0bM9f/58RUdH68MPP/RlRABNECvBAIBGac+ePZKk0NBQtW/fXpL7t/Oduba5VatW+sEPfmBZPgD2RgkGAABA0OFyCAAAAAQdSjAAAACCDiUYAAAAQYcSDAAAgKBDCQYAAEDQoQQDAAAg6FCCAQAAEHQowQAAAAg6/w9vvvg20xV8bQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 19, - "id": "752261bf", - "metadata": {}, - "outputs": [], - "source": [ - "sensor_df = pd.concat(real_sensors, axis=1)" + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkkAAAG5CAYAAACa+qCwAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAeVhJREFUeJzt3Xd8U9X7B/BPkibdg0LbdLd0UMreKggUmSJSFUFEEBXRr/oVxclPAVEUJ+JA1K8KqLhAloshFESQUaDIKLSFlhbogNKZ7uT8/mgTCKTQkSa56ef9euUFubm5OU+T5j495znnyoQQAkRERERkRG7tBhARERHZIiZJRERERCYwSSIiIiIygUkSERERkQlMkoiIiIhMYJJEREREZAKTJCIiIiITmCQRERERmcAkiYiIiMgEJklEREREJjBJIiKbcvjwYYwbNw6hoaFwcnJCYGAghg0bho8++sjaTWsxWVlZmDdvHvr27Ys2bdqgXbt2GDx4MP78809rN42oVZPx2m1EZCt27dqFuLg4hISE4P7774darUZWVhZ2796NkydPIi0tzdpNbBEff/wxnn/+ecTHx6N///6oqanB119/jQMHDuCrr77CAw88YO0mErVKTJKIyGaMHj0a+/btQ0pKCry8vIwey8vLg6+vr3UaZgYajQaurq4mHzt69Cj8/PzQrl07w7bKykp0794dpaWlyMrKslQziegyHG4jIptx8uRJdOrU6aoECYDJBOnbb79Fr1694OzsDG9vb9xzzz1XJRSDBw9G586dcezYMcTFxcHFxQWBgYF4++23rzreRx99hE6dOsHFxQVt2rRB79698d133xntc/DgQYwaNQoeHh5wc3PDLbfcgt27dxvts2zZMshkMmzfvh2PPfYYfH19ERQUVG/cnTp1MkqQAMDR0RG33norzpw5g5KSknqfS0Qth0kSEdmM0NBQ7N+/H0eOHLnuvq+//jqmTJmCqKgoLFy4EE899RS2bNmCgQMHorCw0GjfgoICjBw5Et26dcN7772HmJgYvPDCC/jjjz8M+/zvf//Dk08+idjYWCxatAjz5s1D9+7dsWfPHsM+R48exc0334xDhw7h+eefx+zZs5Geno7Bgwcb7af32GOP4dixY5gzZw5efPHFRv88cnJy4OLiAhcXl0Y/l4jMQBAR2YhNmzYJhUIhFAqFuPHGG8Xz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vb9/QgixZcsWw3v4448/Wrs5Zmfv36Pbtm0TQUFBdvs7KIT9v4fXwySpHps2bRLR0dHi1KlThm2TJk0S/fr1Exs2bLBiy5pv3759IiQkRPTq1cuwraCgQFRUVIiamhohhBA6nc5azTObn376ScTGxooLFy4IIYSoqqoS/fv3F6NHjxZ79uyxcuuapzW8h/b8/glR+x3j4eEhhg4dathWVFRktI/U30N7/h49efKk6Nq1q2jfvr1hW35+vuH30F7Y83vYEPbXb91M+nqHrKws6HQ6owv0Pf/88/D398d7771nreaZRWRkJCZOnAgHBwecOHECr732GoYOHYq4uDjEx8dLvitVp9NBCIHDhw/D09MTTk5OAAClUon58+ejuLgYX331lZVb2TSirg7Ant9De37/Lte1a1eMHj0aPj4+yMzMxKuvvorbbrsNQ4cOxfTp01FSUiLp9xCwz+9R/e9gQEAAHnnkEZSWluLQoUOYP38+BgwYgKFDh2Lw4MHYtWuXlVvaPPb8HjYGkyQAGzZswLp165Ceno6amhoAgEajgVKpxIULFwz7de3aFbfffjsuXLiAFStWWKu5jfbFF19gwoQJhti8vLwwZswYKBQKdOnSBQkJCXjiiSdw5513IjU1FY888gh2795t5VY3zl9//YWjR4+ivLwccrkcMpkMNTU10Gg0EEIYfuEHDx6MuLg4HDlyBH/++aeVW91w2dnZAACZTAYhhN29hwkJCdixYwfOnz8PmUxmd+8fACxbtgzPPPMMtFotAMDPzw933nknjh49is6dO2PLli2YOHEievTogfXr1+PBBx9Eenq6lVvdcJs2bcL27duRn59vqBuzp+/RkydPArj0O+jk5IRRo0ahW7du6NGjB/766y/Mnj0b//3vf6FUKvHMM89g7dq11m10I9n7ubBJrNeJZX27du0SnTt3FhERESI2NlZ07NhRfPLJJ0KI2m5vR0dH8fHHHxs95/Tp0+KWW24RM2fOFNXV1dZodqOUlJQIf39/4enpKd59913D9pqaGvHRRx+J2bNni+zsbMP2w4cPi9jYWPHqq68ahm1sWVJSkujcubMICAgQwcHBYtCgQeK3334TQgiRmpoqFAqF+OWXX4QQwvB+HTt2THTs2FF89NFHVmt3QyUmJopevXqJu+66S2RkZAghhOF9sYf3cOfOnaJr164iIiJCREREiF69ehner9TUVOHg4CDp90/v5MmTwsXFRURHR4uvv/7asL26ulo8//zzYs6cOYZhRSFqv5u8vLzEN998Y43mNsqRI0dEly5dRGBgoAgLCxM9evQQn376qRDCPr5HDxw4IHr37i369esnDhw4IIQQRkPaa9euFS+99JLh91MIIdLT08WwYcPEAw88IMrLy63S7sZoDefCpmqVSZJOpxNffvmlCAwMFC+//LIoKCgQp06dEuPHjxcTJ04UBQUFQgghnn32WREcHCzS09ONnn/PPfeI0aNHG45lyw4ePCiCgoLEU089Jbp27SpOnz5teCw7O1vk5OQY7utjGT16tIiPj7d4WxurvLxcjB8/XkydOlWcOXNG/PPPP2LkyJGie/fuYvv27UIIIcaPHy86deoktFqt0XOHDBkipkyZIoSw3ffwl19+EV27dhV9+vQRYWFhRidX/Zd0dna2UYIklfewpqZGfPjhh8Lf31+8/PLLIicnRxw5ckT07t1bPPfcc0Kr1Yqamhpx7733Svb9u9z27duFWq0W9957r7jttttEbm6u4bGMjAxx9uxZw319PH379hXTpk2zeFsb65FHHhF33XWXuHDhgjhy5Ih47LHHhJ+fn1i7dq0QQoinnnpKst+jmzdvFj169BADBw4U3bt3F/PmzTN8FvX/FhQUiLy8PMNz9LE8+OCDYuDAgZZvdCO0pnNhU7XK4baamhp4e3vjzTffxJw5c+Dh4YHw8HAEBAQgKCgIXl5eAIC5c+eiuroa8+bNQ05OjuH5Qgj4+vpKYqpucXExRo8ejdGjR8PFxQVvvPGG4TG1Wg0/Pz+j/QsLC3HhwgWEhoZauqmNlp2djbVr1+Luu+9GYGAgbrjhBrz11luIiIjAs88+CwCYN28esrKyMGfOHFRXVwOA4V9/f38AsMn3UKfTQaFQYOjQodiwYQO6dOmCb7/9FkeOHAEAw3CGWq2GWq02eq4U3sOKigo4OTnhww8/xCuvvIJ27dqhU6dOiIqKgqenJ+RyORQKBWbNmoUzZ85I7v27UmZmJqZPn474+Hjk5ubiiy++MDwWGhqKgIAAADB8p5w7dw5FRUUICQmxVpMbJD8/Hz/88APi4+PRtm1bdOrUCS+//DJuu+02PPbYYwCAN954A1VVVZL8HvX29kb//v3x/fffY8CAAdiyZQu2bt0K4NLnzsvLCz4+PobnCCFQWVmJnJwchIWF2fR6Qq3pXNhkVkvPrKygoEBUVlYa7n/zzTfCy8tL3H777eKNN94Q+/fvF0IIsX79ehEeHi6GDBkiVqxYIV5//XXRrl078euvv1qr6Q2iz+q//vprceuttwohhJg/f76IjY0Vx44dE0ePHhUXL1407K/VakVFRYV44403RJcuXURiYqJV2t0YycnJolu3bmLlypVG23/99VcREBBg6PL/8ssvhYeHh5gyZYrYsGGDWLBggfD39xd//fWXNZrdYAUFBYa/5P79918RGBgo3n77bUP3/ZV/uUntPczOzjb6HVy+fLlo27atmDZtmvj0009FZmamEEK6758Ql3obFi5caOgVmjZtmhg6dKg4e/asSElJueo5lZWVYt68eaJ3797i+PHjFm1vY128eFH07NlTvPfee0bbjxw5Inx8fMTcuXOFELW9olL8Hq2urjbMOExLSxP9+vUTjz/+uCgsLBRCXP07WFVVJTQajXjrrbdEdHS0SEhIsHSTG83ez4XN1aqSpPrqM2bOnCnc3d3FnDlzxLx588SwYcNEWFiY4Zdj06ZNYsKECaJ///6iU6dONvuhMBXfu+++K55++mkhhBBnz54VAwcOFC4uLkKtVhumdP7xxx/i0UcfFR07dhTt27e32XVodDqdUYz5+fkiNjZWzJ07V2g0GsP28+fPi0cffVQMGjRIlJWVCSFqT7QjRowQ3bp1E9HR0Ya6JVtyZXx6+vH+//73v6Jnz54mkwMpvIf1xSeEEA899JBQKpXihRdeEDNmzBC9evUScXFxorS0VAghxBdffGHz758Q9cf43//+V7z++utCiNoal169eglPT08REBAgzp8/L4QQ4rfffhOPP/64iI2NFe3btxfbtm2zaNsb6vL4SkpKxK233ioefvhhoyHEyspKMXfuXBEUFGR4Dzdv3izZ71H9tjfffFP06dNHrFix4qp9tm7dKmbMmCE6dOggwsPDxebNm1u8rU1l7+dCc7LbJKm0tNSQAV+uoqLC8FdAVVWVEKJ20brLf8EPHTokoqOjxeeff2703Mv3sbaGxCeEEK+88opYuHChKC8vF/fee69QKBSibdu2Rn/55ebmismTJ4vFixdbpO0NVVJSIr788kuRlZVltL2yslIUFxcLIYR48cUXRVBQkKGgUm/x4sWiT58+Rmt7CCGu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", + "text/plain": [ + "
" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "cmp = ms.match(single_obs, network_model)\n", + "cmp.plot()\n", + "cmp.plot.timeseries();" + ] + }, + { + "cell_type": "markdown", + "id": "02e77cf0", + "metadata": {}, + "source": [ + "## Multiple sensors\n", + "\n", + "When you have observations at several nodes you can use `NodeObservation.from_multiple()` to create a list of `NodeObservation` objects. Pass `nodes` as a `dict` mapping each node ID to either a column name/index within a shared `data` source, or to a separate data source entirely:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "752261bf", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:44.147841Z", + "iopub.status.busy": "2026-10-01T14:06:44.147711Z", + "iopub.status.idle": "2026-10-01T14:06:44.151354Z", + "shell.execute_reply": "2026-10-01T14:06:44.150713Z" + } + }, + "outputs": [], + "source": [ + "sensor_df = pd.concat(real_sensors, axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9acfaf6f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:44.152606Z", + "iopub.status.busy": "2026-10-01T14:06:44.152481Z", + "iopub.status.idle": "2026-10-01T14:06:44.348023Z", + "shell.execute_reply": "2026-10-01T14:06:44.347318Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 20, - "id": "9acfaf6f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
nbiasrmseurmsemaeccsir2
observation
water_level@sens11100.0008440.0989450.0989410.0760150.9749860.0005090.950559
water_level@sens2800.0072410.0972850.0970150.0777890.8507820.0005010.721882
water_level@sens389-0.0208130.1046320.1025410.0821040.9360940.0005290.871119
\n", - "
" - ], - "text/plain": [ - " n bias rmse urmse mae cc \\\n", - "observation \n", - "water_level@sens1 110 0.000844 0.098945 0.098941 0.076015 0.974986 \n", - "water_level@sens2 80 0.007241 0.097285 0.097015 0.077789 0.850782 \n", - "water_level@sens3 89 -0.020813 0.104632 0.102541 0.082104 0.936094 \n", - "\n", - " si r2 \n", - "observation \n", - "water_level@sens1 0.000509 0.950559 \n", - "water_level@sens2 0.000501 0.721882 \n", - "water_level@sens3 0.000529 0.871119 " - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
nbiasrmseurmsemaeccsir2
observation
water_level@sens11090.0111330.0914390.0907590.0731170.9827590.0004660.965167
water_level@sens2800.0048110.1065900.1064820.0847230.8543130.0005500.725381
water_level@sens388-0.0016340.1078680.1078560.0880520.0164620.000550-0.068098
\n", + "
" ], - "source": [ - "multi_obs = ms.NodeObservation.from_multiple(data=sensor_df, nodes={30: \"water_level@sens1\", 54: \"water_level@sens2\", 71: \"water_level@sens3\"})\n", - "ms.match(multi_obs, network_model).skill()" + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "water_level@sens1 109 0.011133 0.091439 0.090759 0.073117 0.982759 \n", + "water_level@sens2 80 0.004811 0.106590 0.106482 0.084723 0.854313 \n", + "water_level@sens3 88 -0.001634 0.107868 0.107856 0.088052 0.016462 \n", + "\n", + " si r2 \n", + "observation \n", + "water_level@sens1 0.000466 0.965167 \n", + "water_level@sens2 0.000550 0.725381 \n", + "water_level@sens3 0.000550 -0.068098 " ] - }, + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "multi_obs = ms.NodeObservation.from_multiple(data=sensor_df, nodes=dict(zip(sensor_locations, sensor_df.columns)))\n", + "ms.match(multi_obs, network_model).skill()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "d7a0acf1", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-01T14:06:44.349798Z", + "iopub.status.busy": "2026-10-01T14:06:44.349666Z", + "iopub.status.idle": "2026-10-01T14:06:44.541262Z", + "shell.execute_reply": "2026-10-01T14:06:44.540542Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 21, - "id": "d7a0acf1", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
nbiasrmseurmsemaeccsir2
observation
water_level@sens11100.0008440.0989450.0989410.0760150.9749860.0005090.950559
network_sensor_2800.0072410.0972850.0970150.0777890.8507820.0005010.721882
water_level@sens389-0.0208130.1046320.1025410.0821040.9360940.0005290.871119
\n", - "
" - ], - "text/plain": [ - " n bias rmse urmse mae cc \\\n", - "observation \n", - "water_level@sens1 110 0.000844 0.098945 0.098941 0.076015 0.974986 \n", - "network_sensor_2 80 0.007241 0.097285 0.097015 0.077789 0.850782 \n", - "water_level@sens3 89 -0.020813 0.104632 0.102541 0.082104 0.936094 \n", - "\n", - " si r2 \n", - "observation \n", - "water_level@sens1 0.000509 0.950559 \n", - "network_sensor_2 0.000501 0.721882 \n", - "water_level@sens3 0.000529 0.871119 " - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
nbiasrmseurmsemaeccsir2
observation
water_level@sens11090.0111330.0914390.0907590.0731170.9827590.0004660.965167
network_sensor_2800.0048110.1065900.1064820.0847230.8543130.0005500.725381
water_level@sens388-0.0016340.1078680.1078560.0880520.0164620.000550-0.068098
\n", + "
" ], - "source": [ - "multi_obs = ms.NodeObservation.from_multiple(nodes={30: sensor_1, 54: path_to_sensor2, 71: sensor_3})\n", - "ms.match(multi_obs, network_model).skill()" + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "water_level@sens1 109 0.011133 0.091439 0.090759 0.073117 0.982759 \n", + "network_sensor_2 80 0.004811 0.106590 0.106482 0.084723 0.854313 \n", + "water_level@sens3 88 -0.001634 0.107868 0.107856 0.088052 0.016462 \n", + "\n", + " si r2 \n", + "observation \n", + "water_level@sens1 0.000466 0.965167 \n", + "network_sensor_2 0.000550 0.725381 \n", + "water_level@sens3 0.000550 -0.068098 " ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" } - ], - "metadata": { - "kernelspec": { - "display_name": "modelskill (3.13.13)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.13" - } + ], + "source": [ + "multi_obs = ms.NodeObservation.from_multiple(nodes=dict(zip(sensor_locations, [sensor_1, path_to_sensor2, sensor_3])))\n", + "ms.match(multi_obs, network_model).skill()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "trials-mikeio1d (3.14.5)", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 5 + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/pyproject.toml b/pyproject.toml index 890fc7574..4c3d15ac5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,9 @@ classifiers = [ ] [project.optional-dependencies] -networks = ["mikeio1d", "networkx"] +# networkx and xarray arrive through mikeio1d's own network extra, which +# carries the topology layer this package builds on (ADR-013). +network = ["mikeio1d[network] >= 1.4.0"] [dependency-groups] dev = ["pytest", "plotly >= 4.5", "ruff==0.6.2", "netCDF4", "dask"] @@ -62,7 +64,7 @@ test = [ notebooks = ["nbformat", "nbconvert", "jupyter", "plotly", "shapely", "seaborn"] -networks = ["mikeio1d>=1.2.1", "networkx"] +network = ["mikeio1d[network] >= 1.4.0"] [project.urls] "Homepage" = "https://github.com/DHI/modelskill" diff --git a/roadmap/features/network-models.md b/roadmap/features/network-models.md index 2190befb9..f0da102ca 100644 --- a/roadmap/features/network-models.md +++ b/roadmap/features/network-models.md @@ -26,3 +26,5 @@ In active development. MIKE 1D, MIKE 11 and EPANET result files can be read toda MOUSE and Water Hammer results are not read yet: no shareable result file exists for either format, so support cannot be verified. SWMM results are not read yet: the reach connectivity lives in the companion '.inp' input file, which modelskill does not read yet. +The layer that reads those files and builds the network lives in mikeio1d, where the formats, the fixtures and a first graph conversion already were (ADR-013). ModelSkill keeps the model result, the observations and the matching, and requires `mikeio1d[network]`. mikeio1d 1.4.0 is the first release with that module. + diff --git a/src/modelskill/comparison/_comparison.py b/src/modelskill/comparison/_comparison.py index 8838184e5..8f5de5f07 100644 --- a/src/modelskill/comparison/_comparison.py +++ b/src/modelskill/comparison/_comparison.py @@ -25,10 +25,23 @@ from .. import metrics as mtr from .. import Quantity -from ..types import GeometryType -from ..obs import PointObservation, TrackObservation, NodeObservation +from ..types import GeometryType, network_gtype +from ..obs import ( + PointObservation, + TrackObservation, + NodeObservation, + ReachObservation, + _at_from_coords, +) from ..model import PointModelResult, TrackModelResult, VerticalModelResult -from ..timeseries._timeseries import _normalize_time_to_ns, _validate_data_var_name +from ..timeseries._coords import ( + NETWORK_LOCATION_COORDS, + _coordinate_values, +) +from ..timeseries._timeseries import ( + _normalize_time_to_ns, + _validate_data_var_name, +) from ._comparer_plotter import ComparerPlotter from ..metrics import _parse_metric @@ -54,7 +67,7 @@ def _drop_scalar_coords(data: xr.Dataset) -> xr.Dataset: """Drop scalar coordinate variables that shouldn't appear as columns in dataframes""" - coords_to_drop = ["x", "y", "z", "node"] + coords_to_drop = ["x", "y", "z", *NETWORK_LOCATION_COORDS] return data.drop_vars(coords_to_drop, errors="ignore") @@ -72,8 +85,8 @@ def _parse_dataset(data: xr.Dataset) -> xr.Dataset: # coordinates # Only add x, y, z coordinates if they don't exist and we don't have node coordinates - has_node_coords = "node" in data.coords - if not has_node_coords: + has_network_coords = network_gtype(data) is not None + if not has_network_coords: if "x" not in data.coords: data.coords["x"] = np.nan if "y" not in data.coords: @@ -112,10 +125,7 @@ def _parse_dataset(data: xr.Dataset) -> xr.Dataset: # Validate attrs if "gtype" not in data.attrs: # Determine gtype based on available coordinates - if "node" in data.coords: - data.attrs["gtype"] = str(GeometryType.NODE) - else: - data.attrs["gtype"] = str(GeometryType.POINT) + data.attrs["gtype"] = str(network_gtype(data) or GeometryType.POINT) # assert "gtype" in data.attrs, "data must have a gtype attribute" # assert data.attrs["gtype"] in [ # str(GeometryType.POINT), @@ -545,9 +555,22 @@ def from_matched_data( # FIXME: Consider changes needed for vertical return Comparer(matched_data=data, raw_mod_data=raw_mod_data) + def _location_repr(self) -> str | None: + """The location line for ``__repr__``, or None when there is nothing to say.""" + if self.gtype == str(GeometryType.NODE): + return f"Location: {self.at}" + if self.gtype == str(GeometryType.REACH): + return f"Location: {self.reach}" + if self.gtype == str(GeometryType.POINT): + if self.x is not None and self.y is not None: + return f"Location: {self.x}, {self.y}" + return None + def __repr__(self): - out = [ - "", + out = [""] + if (location := self._location_repr()) is not None: + out.append(location) + out += [ f"Quantity: {self.quantity}", f"Observation: {self.name}, n_points={self.n_points}", "Model(s):", @@ -640,17 +663,35 @@ def z(self) -> Any: """z-coordinate""" return self._coordinate_values("z") + @property + def at(self) -> str | tuple[str, float] | None: + """Where a network comparer sits: the observation's location. + + A node name, a ``(reach_id, position)`` breakpoint, or a reach name. + None for a comparer that is not in a network. Its raw model results + sit there too; where a model was read is + ``NetworkModelResult.extract(obs).at``. + """ + return _at_from_coords(self.data) if self.gtype in ("node", "reach") else None + @property def node(self) -> Any: - """node-coordinate""" + """Name of the node this comparer sits at, or None for a break point""" return self._coordinate_values("node") - def _coordinate_values(self, coord: str) -> None | Any: + @property + def reach(self) -> Any: + """Name of the reach this comparer sits on""" + return self._coordinate_values("reach") + + @property + def position(self) -> Any: + """Along-reach position of the observation's breakpoint, or None for a node or a reach""" + return self._coordinate_values("position") + + def _coordinate_values(self, coord: str) -> Any: """Get coordinate values if they exist, otherwise return None""" - if coord not in self.data.coords: - return None - vals = self.data[coord].values - return np.atleast_1d(vals)[0] if vals.ndim == 0 else vals + return _coordinate_values(self.data, coord) @property def n_models(self) -> int: @@ -773,7 +814,9 @@ def rename( return Comparer(matched_data=data, raw_mod_data=raw_mod_data) - def _to_observation(self) -> PointObservation | TrackObservation | NodeObservation: + def _to_observation( + self, + ) -> PointObservation | TrackObservation | NodeObservation | ReachObservation: """Convert to Observation""" if self.gtype == "point": df = _drop_scalar_coords(self.data)[self._obs_str].to_dataframe() @@ -804,7 +847,16 @@ def _to_observation(self) -> PointObservation | TrackObservation | NodeObservati return NodeObservation( data=df, name=self.name, - at=self.node, + at=_at_from_coords(self.data), + quantity=self.quantity, + # TODO: add attrs + ) + elif self.gtype == "reach": + df = _drop_scalar_coords(self.data)[self._obs_str].to_dataframe() + return ReachObservation( + data=df, + name=self.name, + reach=self.reach, quantity=self.quantity, # TODO: add attrs ) @@ -1012,9 +1064,9 @@ def _to_long_dataframe( """Return a copy of the data as a long-format pandas DataFrame (for groupby operations)""" if self.gtype == "vertical": - data = self.data.drop_vars("node", errors="ignore") + data = self.data.drop_vars(NETWORK_LOCATION_COORDS, errors="ignore") else: - data = self.data.drop_vars(["z", "node"], errors="ignore") + data = self.data.drop_vars(["z", *NETWORK_LOCATION_COORDS], errors="ignore") # this step is necessary since we keep arbitrary derived data in the dataset, but not z/node # i.e. using a hardcoded whitelist of variables to keep is less flexible @@ -1338,8 +1390,9 @@ def to_dataframe(self) -> pd.DataFrame: + ["z"] ) return df[cols] - elif self.gtype == str(GeometryType.NODE): - # For network data, drop node coordinate like other geometries drop their coordinates + elif self.gtype in (str(GeometryType.NODE), str(GeometryType.REACH)): + # For network data, drop the location coordinates like other geometries + # drop theirs return _drop_scalar_coords(self.data).to_dataframe() else: raise NotImplementedError(f"Unknown gtype: {self.gtype}") @@ -1361,7 +1414,7 @@ def save(self, filename: Union[str, Path]) -> None: # https://docs.xarray.dev/en/stable/user-guide/io.html#groups # There is no need to save raw data for track data, since it is identical to the matched data - if self.gtype in ("point", "node"): + if self.gtype in ("point", "node", "reach"): ds = self.data.copy() # copy needed to avoid modifying self.data for key, ts_mod in self.raw_mod_data.items(): @@ -1369,7 +1422,9 @@ def save(self, filename: Union[str, Path]) -> None: # rename time to unique name ts_mod.data = ts_mod.data.rename({"time": "_time_raw_" + key}) # da = ds_mod.to_xarray()[key] - ds["_raw_" + key] = ts_mod.data[key] + ds["_raw_" + key] = ts_mod.data[key].drop_vars( + NETWORK_LOCATION_COORDS, errors="ignore" + ) ds.to_netcdf(filename) @@ -1396,7 +1451,7 @@ def load(filename: Union[str, Path]) -> "Comparer": # FIXME: consider during Phase3 return Comparer(matched_data=data) - if data.gtype in ("point", "node"): + if data.gtype in ("point", "node", "reach"): raw_mod_data: Dict[ str, PointModelResult @@ -1413,12 +1468,10 @@ def load(filename: Union[str, Path]) -> "Comparer": {"_time_raw_" + new_key: "time", var_name: new_key} ) ts: PointModelResult | NodeModelResult - if data.gtype == "node": - ts = NodeModelResult( - data=ds, node=int(ds.coords["node"].item()), name=new_key - ) + if data.gtype in ("node", "reach"): + ts = NodeModelResult(data=ds) else: - ts = PointModelResult(data=ds, name=new_key) + ts = PointModelResult(data=ds) raw_mod_data[new_key] = ts diff --git a/src/modelskill/matching.py b/src/modelskill/matching.py index b7ac4b983..38e5c913a 100644 --- a/src/modelskill/matching.py +++ b/src/modelskill/matching.py @@ -39,6 +39,7 @@ ReachObservation, ) from .timeseries import TimeSeries +from .timeseries._coords import NETWORK_LOCATION_COORDS from .types import Period TimeDeltaTypes = Union[float, int, np.timedelta64, pd.Timedelta, timedelta] @@ -360,7 +361,7 @@ def _match_single_obs( if is_field or is_dummy: matching_obs = m.extract(obs, spatial_method=spatial_method) elif is_network: - matching_obs = m.extract(obs) + matching_obs = _at_observation(m.extract(obs), obs) else: matching_obs = m @@ -407,6 +408,18 @@ def _check_timezone_compatibility( ) +def _at_observation(mr: NodeModelResult, obs: TimeSeries) -> NodeModelResult: + """The extracted model result, placed where the observation sits. + + A comparer has one location, the observation's: with several models, each + may be read at a different breakpoint. Where a model was read is + ``NetworkModelResult.extract(obs).at``. + """ + location = {c: obs.data[c] for c in NETWORK_LOCATION_COORDS if c in obs.data.coords} + data = mr.data.drop_vars(NETWORK_LOCATION_COORDS, errors="ignore") + return NodeModelResult(data.assign_coords(location)) + + def _match_space_time( observation: Observation, raw_mod_data: Mapping[ @@ -444,11 +457,7 @@ def _match_space_time( "max_model_gap is not yet supported for VerticalModelResult / VerticalObservation matching" ) aligned = vmr.align(observation) - case NodeModelResult() as nmr, NodeObservation(): - # mr is the extracted NodeModelResult - aligned = align_data(nmr.data, observation, max_gap=max_model_gap) - case NodeModelResult() as nmr, ReachObservation(): - # ReachObservation is extracted to a NodeModelResult (any breakpoint on the reach) + case NodeModelResult() as nmr, NodeObservation() | ReachObservation(): aligned = align_data(nmr.data, observation, max_gap=max_model_gap) case _: raise TypeError( diff --git a/src/modelskill/model/__init__.py b/src/modelskill/model/__init__.py index 22c81857a..e81ca8d90 100644 --- a/src/modelskill/model/__init__.py +++ b/src/modelskill/model/__init__.py @@ -9,7 +9,7 @@ * SpatialField (extractable) - [`GridModelResult`](`modelskill.GridModelResult`) - a spatial field from a dfs2/nc file or a Xarray Dataset - [`DfsuModelResult`](`modelskill.DfsuModelResult`) - a spatial field from a dfsu file - - [`NetworkModelResult`](`modelskill.NetworkModelResult`) - a network field from xarray Dataset with time and node coordinates + - [`NetworkModelResult`](`modelskill.NetworkModelResult`) - a 1D network result from a res1d/res11/res file or a mikeio1d Network A model result can be created by explicitly invoking one of the above classes or using the [`model_result()`](`modelskill.model_result`) function which will return the appropriate type based on the input data (if possible). """ diff --git a/src/modelskill/model/adapters/__init__.py b/src/modelskill/model/adapters/__init__.py deleted file mode 100644 index 33ad255b1..000000000 --- a/src/modelskill/model/adapters/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Network format adapters.""" diff --git a/src/modelskill/model/adapters/_inp.py b/src/modelskill/model/adapters/_inp.py deleted file mode 100644 index 329b2c92a..000000000 --- a/src/modelskill/model/adapters/_inp.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Minimal reader for EPANET and SWMM ``.inp`` input files. - -mikeio1d reads only the binary result formats, so the ``.inp`` that accompanies a -result file has to be parsed here. Both products use the same layout: bracketed -section headers, ``;``-prefixed comments (including the ``;;Name Node1 ...`` -column headers the products write), whitespace-delimited data rows, and blank -lines to ignore. - -Only the sections modelskill needs are interpreted; everything else is kept as -raw fields for a caller to use, or ignored. -""" - -from __future__ import annotations - -from pathlib import Path - - -def read_sections(path: str | Path) -> dict[str, list[list[str]]]: - """Parse an ``.inp`` file into its sections. - - Parameters - ---------- - path : str or Path - Path to an EPANET or SWMM ``.inp`` file. - - Returns - ------- - dict[str, list[list[str]]] - Section name (upper case, without brackets) mapped to its data rows, - each row split into whitespace-delimited fields. Comment-only and blank - lines are dropped, as is any trailing comment on a data row. - - Examples - -------- - >>> sections = read_sections("model.inp") # doctest: +SKIP - >>> sections["PIPES"][0] # doctest: +SKIP - ['10', '10', '11', '3209.544', '304.8', '100', '0', 'Open'] - """ - sections: dict[str, list[list[str]]] = {} - current: list[list[str]] | None = None - - with open(path, "r", encoding="utf-8", errors="replace") as f: - for line in f: - # A comment can trail a data row, so strip it before anything else. - line = line.split(";", 1)[0].strip() - if not line: - continue - - if line.startswith("["): - name = line.strip("[]").strip().upper() - current = sections.setdefault(name, []) - continue - - if current is not None: - current.append(line.split()) - - return sections - - -def read_pipe_lengths(path: str | Path) -> dict[str, float]: - """Read reach lengths from the ``[PIPES]`` section of an EPANET ``.inp``. - - Parameters - ---------- - path : str or Path - Path to an EPANET ``.inp`` file. - - Returns - ------- - dict[str, float] - Pipe ID mapped to its length. Pumps and valves are links too, but carry - no length, so they are absent from the result rather than present with a - placeholder. - - Raises - ------ - ValueError - If the file has no ``[PIPES]`` section, or a row there has too few - fields to read a length from. - - Notes - ----- - ``[PIPES]`` rows are ``ID Node1 Node2 Length Diameter Roughness ...``, so the - length is the fourth field. The units are whatever the model declares in - ``[OPTIONS]``; no conversion is applied. - """ - sections = read_sections(path) - - try: - rows = sections["PIPES"] - except KeyError: - raise ValueError( - f"'{path}' has no [PIPES] section, so it does not look like an " - "EPANET input file. Available sections: " - f"{sorted(sections)}." - ) - - _ID, _LENGTH = 0, 3 - lengths: dict[str, float] = {} - for row in rows: - if len(row) <= _LENGTH: - raise ValueError( - f"Cannot read a pipe length from [PIPES] row {' '.join(row)!r} " - f"in '{path}': expected at least {_LENGTH + 1} fields " - f"(ID, Node1, Node2, Length), got {len(row)}." - ) - lengths[row[_ID]] = float(row[_LENGTH]) - - return lengths diff --git a/src/modelskill/model/adapters/_res1d.py b/src/modelskill/model/adapters/_res1d.py deleted file mode 100644 index 567f0d498..000000000 --- a/src/modelskill/model/adapters/_res1d.py +++ /dev/null @@ -1,189 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import pandas as pd - -if TYPE_CHECKING: - from mikeio1d.result_network import ResultNode, ResultGridPoint, ResultReach - -from modelskill.network import NetworkNode, ReachBreakPoint, NetworkReach - - -def _simplify_colnames(node: ResultNode | ResultGridPoint) -> pd.DataFrame: - # We remove suffixes and indexes so the columns contain only the quantity names - - # Some formats keep no timeseries at all on some locations - MIKE 11, for instance, - # stores everything on reach gridpoints, leaving the nodes empty. Asking mikeio1d - # for a dataframe there raises, so return an empty one instead. - if not node.quantities: - return pd.DataFrame() - - # The columns in a Res1D dataframe follow the convention "Quantity:Location:Sublocation" - # where Location refers to the node id or the reach id followed by the chainage. - RES1D_NAME_SEP = ":" - df = node.to_dataframe() - renamer_dict = {} - for quantity in node.quantities: - column_pairs = [ - (col, quantity) - for col in df.columns - if quantity in col.split(RES1D_NAME_SEP) - ] - if len(column_pairs) != 1: - raise ValueError( - f"There must be exactly one column per quantity, found {column_pairs}." - ) - old_name, new_name = column_pairs[0] - renamer_dict[old_name] = new_name - return df.rename(columns=renamer_dict).copy() - - -def _merge_extra_quantities( - base: pd.DataFrame, extra: pd.DataFrame, *, node_id: str -) -> pd.DataFrame: - """Append a companion file's quantities to a node's frame as extra columns. - - Parameters - ---------- - base : pd.DataFrame - The node's frame from the main result file. - extra : pd.DataFrame - The same node's frame from the companion file, sharing its time index. - node_id : str - Node ID, used in error messages. - - Returns - ------- - pd.DataFrame - - Raises - ------ - ValueError - If a quantity appears in both frames. Concatenating would give the node - two columns of the same name, which is the state ``_simplify_colnames`` - already refuses. - """ - if extra.empty: - return base - - overlapping = base.columns.intersection(extra.columns) - if len(overlapping) > 0: - raise ValueError( - f"Node {node_id!r} already has {sorted(overlapping)} in the main " - "result file, so the companion file's copy cannot be merged in." - ) - - return pd.concat([base, extra], axis=1) - - -class Res1DNode(NetworkNode): - def __init__( - self, - id: str, - *, - data: pd.DataFrame | None = None, - boundary: dict[str, pd.DataFrame] | None = None, - ): - self._id = id - self._data = pd.DataFrame() if data is None else data - self._boundary = {} if boundary is None else boundary - - @property - def id(self) -> str: - return self._id - - @property - def data(self) -> pd.DataFrame: - return self._data - - @property - def boundary(self) -> dict[str, pd.DataFrame]: - return self._boundary - - -class GridPoint(ReachBreakPoint): - def __init__( - self, reach_id: str, chainage: float, data: pd.DataFrame | None = None - ): - self._id = (reach_id, chainage) - self._data = pd.DataFrame() if data is None else data - - @property - def id(self) -> tuple[str, float]: - return self._id - - @property - def data(self) -> pd.DataFrame: - return self._data - - -class Res1DReach(NetworkReach): - """NetworkReach adapter for a mikeio1d ResultReach.""" - - def __init__( - self, - reach: ResultReach, - start_node: Res1DNode, - end_node: Res1DNode, - *, - populate_gridpoints: bool = True, - length: float | None = None, - ): - self._id = reach.name - - # Must be checked separately: some formats (.resx) report None for both the - # reach and the node, which the identity checks below would let through. - if reach.start_node is None or reach.end_node is None: - raise ValueError( - f"mikeio1d reported no start/end node for reach {reach.name!r}; " - "this result format's topology cannot be represented as a Network." - ) - - if start_node.id != reach.start_node: - raise ValueError("Incorrect starting node.") - if end_node.id != reach.end_node: - raise ValueError("Incorrect ending node.") - - intermediate_gridpoints = ( - reach.gridpoints[1:-1] if len(reach.gridpoints) > 2 else [] - ) - - self._start = start_node - self._end = end_node - - # A length read from a companion input file wins, since mikeio1d has none - # to offer for the formats that need one. Otherwise: mikeio1d returns 0 - # when it cannot read a reach length - link-node models such as EPANET - # report this for every reach. Report it as undefined rather than as a - # zero-length reach, which would make length-weighted graph algorithms - # treat the reach as free. The two cases cannot be told apart upstream. - self._length = length if length is not None else (reach.length or None) - self._breakpoints: list[ReachBreakPoint] = [ - GridPoint( - gridpoint.reach_name, - gridpoint.chainage, - _simplify_colnames(gridpoint) if populate_gridpoints else None, - ) - for gridpoint in intermediate_gridpoints - ] - - @property - def id(self) -> str: - return self._id - - @property - def start(self) -> Res1DNode: - return self._start - - @property - def end(self) -> Res1DNode: - return self._end - - @property - def length(self) -> float | None: - return self._length - - @property - def breakpoints(self) -> list[ReachBreakPoint]: - return self._breakpoints diff --git a/src/modelskill/model/factory.py b/src/modelskill/model/factory.py index ebec31316..c2bc0f744 100644 --- a/src/modelskill/model/factory.py +++ b/src/modelskill/model/factory.py @@ -10,6 +10,7 @@ from .vertical import VerticalModelResult from .dfsu import DfsuModelResult from .grid import GridModelResult +from .network import NetworkModelResult from ..types import GeometryType, DataInputType @@ -20,6 +21,7 @@ GeometryType.VERTICAL: VerticalModelResult, GeometryType.UNSTRUCTURED: DfsuModelResult, GeometryType.GRID: GridModelResult, + GeometryType.NETWORK: NetworkModelResult, } @@ -27,7 +29,8 @@ def model_result( data: DataInputType, *, aux_items: list[int | str] | None = None, - gtype: Literal["point", "track", "vertical", "unstructured", "grid"] | None = None, + gtype: Literal["point", "track", "vertical", "unstructured", "grid", "network"] + | None = None, **kwargs: Any, ) -> ( PointModelResult @@ -35,6 +38,7 @@ def model_result( | VerticalModelResult | DfsuModelResult | GridModelResult + | NetworkModelResult ): """A factory function for creating an appropriate object based on the data input. @@ -44,7 +48,7 @@ def model_result( The data to be used for creating the ModelResult object. aux_items : list[int | str] | None Auxiliary items, by default None - gtype : Literal["point", "track", "vertical", "unstructured", "grid"] | None + gtype : Literal["point", "track", "vertical", "unstructured", "grid", "network"] | None The geometry type of the data. If not specified, it will be guessed from the data. **kwargs Additional keyword arguments to be passed to the ModelResult constructor. @@ -72,6 +76,9 @@ def model_result( def _guess_gtype(data: Any) -> GeometryType: + if _is_network(data): + return GeometryType.NETWORK + if hasattr(data, "geometry"): geom_str = repr(data.geometry).lower() if "flex" in geom_str: @@ -96,6 +103,8 @@ def _guess_gtype(data: Any) -> GeometryType: return GeometryType.GRID elif file_ext == ".dfs2": return GeometryType.GRID + elif file_ext in (".res1d", ".res11", ".res"): + return GeometryType.NETWORK else: raise ValueError( "Could not guess gtype from file extension, please specify gtype, e.g. gtype='track'" @@ -114,3 +123,12 @@ def _guess_gtype(data: Any) -> GeometryType: raise ValueError( f"Geometry type (gtype) could not be guessed from this type of data: {type(data)}. Please specify gtype, e.g. gtype='track'" ) + + +def _is_network(data: Any) -> bool: + # mikeio1d is optional (ADR-010): without it, nothing is a Network. + try: + from mikeio1d.network import Network + except ImportError: + return False + return isinstance(data, Network) diff --git a/src/modelskill/model/network.py b/src/modelskill/model/network.py index 328c1cdea..a66aeaa3b 100644 --- a/src/modelskill/model/network.py +++ b/src/modelskill/model/network.py @@ -1,107 +1,125 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from datetime import datetime +from pathlib import Path +from typing import TYPE_CHECKING, Any, Sequence import numpy as np -import numpy.typing as npt import pandas as pd import xarray as xr -from modelskill.timeseries import TimeSeries, _parse_network_node_input +from modelskill.timeseries import TimeSeries, _parse_point_input +from modelskill.timeseries._coords import NetworkCoords from ._base import SelectedItems -from ..obs import NodeObservation, ReachObservation +from ..obs import NodeObservation, ReachObservation, _at_from_coords from ..quantity import Quantity -from ..types import PointType +from ..types import network_gtype if TYPE_CHECKING: - from modelskill.network import Network + from mikeio1d.network import Address, Network, NetworkLocation + + +def _network_class() -> type[Network]: + # Imported here, not at module scope, so this module stays importable + # without the optional network dependencies (ADR-010). + try: + from mikeio1d.network import Network + except ImportError as err: + raise ImportError( + "NetworkModelResult needs the network topology layer from mikeio1d, " + "which the 'network' extra installs: pip install modelskill[network]" + ) from err + return Network + + +def _no_data_error(address: Address, item: str) -> ValueError: + return ValueError( + f"{address!r} was found in the network but has no data for " + f"quantity '{item}'. Choose a location that has this quantity, or a " + "model result for a quantity this location has." + ) class NodeModelResult(TimeSeries): - """Model result for a single network node. + """Model result at one network location. - Construct a NodeModelResult from timeseries data for a specific node. - This is a simple timeseries class designed for network node data. + What :meth:`NetworkModelResult.extract` returns: the timeseries of a single + node or break point, carrying in its coordinates the location it was taken + from. Extract one from a :class:`NetworkModelResult` rather than building it + directly, since a location is named by the network it belongs to. Parameters ---------- - data : str, Path, mikeio.Dataset, mikeio.DataArray, pd.DataFrame, pd.Series, xr.Dataset or xr.DataArray - filename (.dfs0 or .nc) or object with the data - name : str, optional - The name of the model result, - by default None (will be set to file name or item name) - node : int, optional - node ID (integer), by default None - item : str | int | None, optional - If multiple items/arrays are present in the input an item - must be given (as either an index or a string), by default None - quantity : Quantity, optional - Model quantity, for MIKE files this is inferred from the EUM information - aux_items : list[int | str], optional - Auxiliary items, by default None - - Examples + data : xr.Dataset + Timeseries for one location, carrying a ``node`` coordinate, or a + ``reach`` coordinate with ``position`` for a break point. + + Raises + ------ + TypeError + If data is not an xarray.Dataset. + ValueError + If data carries no network location. + + See Also -------- - >>> import modelskill as ms - >>> mr = ms.NodeModelResult(data, node=123, name="Node_123") - >>> mr2 = ms.NodeModelResult(df, item="Water Level", node=456) + NetworkModelResult.extract : Extract a model result at a node or a reach. """ - def __init__( - self, - data: PointType, - node: int, - *, - name: str | None = None, - item: str | int | None = None, - quantity: Quantity | None = None, - aux_items: Sequence[int | str] | None = None, - ): - if not self._is_input_validated(data): - data = _parse_network_node_input( - data, - name=name, - item=item, - quantity=quantity, - node=node, - aux_items=aux_items, - ) - + def __init__(self, data: xr.Dataset) -> None: if not isinstance(data, xr.Dataset): - raise ValueError("'NodeModelResult' requires xarray.Dataset") - if data.coords.get("node") is None: - raise ValueError("'node' coordinate not found in data") + raise TypeError( + "'NodeModelResult' takes an xarray.Dataset carrying its own " + f"location, got {type(data).__name__}. A model result for a " + "network location comes from NetworkModelResult.extract()." + ) + if network_gtype(data) is None: + raise ValueError( + "'NodeModelResult' needs data carrying a 'node' coordinate, or a " + "'reach' coordinate for a reach or a break point. A model result " + "for a network location comes from NetworkModelResult.extract()." + ) + # Mark the kind on our own copy. The dataset handed in may belong to an + # observation, and a shallow copy rebuilds each variable's attrs, so + # reading it here does not turn that observation into a model result. + data = data.copy() data_var = str(list(data.data_vars)[0]) data[data_var].attrs["kind"] = "model" super().__init__(data=data) @property - def node(self) -> int: - """Node ID of model result""" - node_val = self.data.coords["node"] - return int(node_val.item()) + def at(self) -> str | tuple[str, float]: + """Where this result was extracted: a node name, or a ``(reach_id, position)`` breakpoint.""" + return _at_from_coords(self.data) - def _create_new_instance(self, data: xr.Dataset) -> NodeModelResult: - """Extract node from data and create new instance""" - node = int(data.coords["node"].item()) - return self.__class__(data, node=node) + @property + def node(self) -> Any: + """Name of the node this result was extracted at, or None for a break point.""" + return self._coordinate_values("node") + + def _location_repr(self) -> str | None: + return f"Location: {self.at}" class NetworkModelResult: - """Model result for network data with time and node dimensions. + """Model result for a network of nodes and reaches. + + Construct one from a result file, or from a :class:`mikeio1d.network.Network` + already built. Observations name the location they sit at, and no spatial + interpolation is performed. - Construct a NetworkModelResult from a Network object containing - timeseries data for each node. Users must provide exact node IDs - (integers obtained via ``Network.find()``) when creating observations — - no spatial interpolation is performed. + Nothing is read when the model result is built. :meth:`extract` reads the + series of the one location an observation names, so matching a handful of + sensors against a large model reads a handful of locations. Parameters ---------- - data : Network - Network-like object with a ``to_dataset()`` method (e.g. :class:`modelskill.network.Network`). + data : Network, str or Path + Path to a ``.res1d``, ``.res11`` or ``.res`` result file, or a + :class:`mikeio1d.network.Network`. name : str, optional The name of the model result, - by default None (will be set to first data variable name) + by default None (will be set to the item name) item : str | int | None, optional If multiple items/arrays are present in the input an item must be given (as either an index or a string), by default None @@ -113,56 +131,78 @@ class NetworkModelResult: Examples -------- >>> import modelskill as ms - >>> from modelskill.network import Network - >>> network = Network(reaches) # reaches is a list[NetworkReach] - >>> mr = ms.NetworkModelResult(network, name="MyModel") - >>> obs = ms.NodeObservation(data, node=network.find(node="node_A")) + >>> mr = ms.NetworkModelResult("model.res1d", item="WaterLevel") + >>> obs = ms.NodeObservation(data, at="node_A") >>> extracted = mr.extract(obs) + + Open the network yourself to name an EPANET .resx companion: + + >>> from mikeio1d.network import Network + >>> network = Network.open("model.res", companions=["model.resx"]) + >>> mr = ms.NetworkModelResult(network, item="Head", name="MyModel") + + Notes + ----- + The network is used as given, not copied, so ``mr.network`` is the caller's + object. It reads from its result file on every :meth:`extract`. + + See Also + -------- + mikeio1d.network.Network.open : Read a network from a result file. """ def __init__( self, - data: Network, + data: Network | str | Path, *, name: str | None = None, item: str | int | None = None, quantity: Quantity | None = None, aux_items: Sequence[int | str] | None = None, ): - self.network = data.copy() + network_class = _network_class() + if isinstance(data, (str, Path)): + self.network = network_class.open(data) + elif isinstance(data, network_class): + self.network = data + else: + raise TypeError( + "NetworkModelResult takes a mikeio1d.network.Network or a path to a " + f"result file, got {type(data).__name__}" + ) - ds = self.network.to_dataset() - sel_items = SelectedItems.parse( - list(ds.data_vars), item=item, aux_items=aux_items - ) + # What the network can read somewhere, which it knows without reading. + units = self.network.quantities + sel_items = SelectedItems.parse(list(units), item=item, aux_items=aux_items) name = name or sel_items.values - self.data = ds[sel_items.all] self.name = name self.sel_items = sel_items if quantity is None: - da = self.data[sel_items.values] - quantity = Quantity.from_cf_attrs(da.attrs) + # A result file names its quantity and, mostly, its unit. Where it + # gives no unit the name is still worth keeping, so the unit is left + # empty rather than the quantity undefined. + quantity = Quantity( + name=str(sel_items.values), unit=units[sel_items.values] or "" + ) self.quantity = quantity - # Mark data variables as model data - self.data[sel_items.values].attrs["kind"] = "model" - def __repr__(self) -> str: return f"<{self.__class__.__name__}>: {self.name}" - _CHAINAGE_TOLERANCE = 1e-3 # Tolerance in source-network distance units (e.g., meters if chainage is in meters). - @property - def time(self) -> pd.DatetimeIndex: - """Return the time coordinate as a pandas.DatetimeIndex.""" - return pd.DatetimeIndex(self.data.time.to_index()) + def period(self) -> tuple[datetime, datetime]: + """First and last timestep of the result file. - @property - def nodes(self) -> npt.NDArray[np.intp]: - """Return the node IDs as a numpy array of integers.""" - return self.data.node.values + Read from the file header, so no timeseries is read. + + Returns + ------- + tuple[datetime, datetime] + Start and end of the result file's time axis. + """ + return self.network.period def extract( self, @@ -173,7 +213,7 @@ def extract( Parameters ---------- observation : NodeObservation or ReachObservation - observation with node ID or reach ID + observation naming a node, a breakpoint, or a reach Returns ------- @@ -181,7 +221,33 @@ def extract( extracted model result """ if isinstance(observation, NodeObservation): - return self._extract_node(observation) + # Resolving with the item snaps onto a breakpoint that carries it, + # which matters on a staggered grid. Resolving again without it + # tells a location that lacks the item from one that is not there. + item = self.sel_items.values + tol = observation.position_tol + found = self.network.resolve( + observation.at, position_tol=tol, quantity=item + ) + if ( + found is None + and self.network.resolve(observation.at, position_tol=tol) is not None + ): + raise _no_data_error(observation.at, item) + if found is None: + hint = ( + " NodeObservation(position_tol=...) widens how far a position " + "may be from a break point." + if isinstance(observation.at, tuple) + else "" + ) + raise ValueError( + f"Location {observation.at!r} not found in the network. " + "network.addresses() lists the nodes and break points it has, " + "and addresses(reach=...) the break points along one reach." + hint + ) + [df] = self._read([found]) + return self._to_result(found.address, df) elif isinstance(observation, ReachObservation): return self._extract_reach(observation) else: @@ -189,137 +255,85 @@ def extract( f"NetworkModelResult supports NodeObservation and ReachObservation, got {type(observation).__name__}" ) - def _extract_node(self, observation: NodeObservation) -> NodeModelResult: - node_id = self._resolve_alias(observation.at) - - available_nodes = set(self.data.node.values) - if node_id not in available_nodes: - raise ValueError( - f"Node {node_id} exists in the network topology but its timeseries was not loaded. " - f"Re-create the NetworkModelResult with the relevant nodes populated, " - f"e.g. Network.from_mike(path, nodes=[...])." - ) - - return NodeModelResult( - data=self.data.sel(node=node_id).drop_vars("node"), - node=node_id, - name=self.name, - item=self.sel_items.values, - quantity=self.quantity, - aux_items=self.sel_items.aux, - ) - def _extract_reach(self, observation: ReachObservation) -> NodeModelResult: - # Extract model result from an arbitrary breakpoint belonging to the reach. - - # Searches the alias map for breakpoints whose reach component matches - # ``observation.reach``, then returns the first one that has data in the - # dataset. Raises if no breakpoint with data is found or if the quantity - # is not present for any breakpoint of that reach. - + # A reach observation matches any breakpoint along the reach, so long as + # they agree. The network says which breakpoints carry the quantity, and + # only those are read, all in one read. item = self.sel_items.values reach_id = observation.reach - try: - reach = self.network._reaches[reach_id] - except KeyError: - raise ValueError(f"Reach {reach_id} not found in network.") - - # This only searches intermediate breakpoints since reach-level data is not - # expected in nodes. - - available_nodes = {int(node_id) for node_id in self.data.node.values} - found_ds = None - found_int_id: int | None = None - missing_node_data = False - for breakpoint in reach.breakpoints: - if breakpoint.data is None: - continue - if item not in breakpoint.data.columns: - continue - - int_id = self.network.find( - reach=breakpoint.id[0], distance=breakpoint.distance + # addresses() raises a KeyError naming the reach if the network has none + # such, and every point it gives resolves. + found = [ + self.network.resolve(point) + for point in self.network.addresses(reach=reach_id, quantity=item) + ] + if not found: + raise ValueError( + f"Reach '{reach_id}' was found in the network but none of its " + f"breakpoints carry quantity '{item}'. Choose a reach that has " + "this quantity, or a model result for a quantity this reach has." ) - if int_id not in available_nodes: - missing_node_data = True - continue - - ds = self.data.sel(node=int_id).drop_vars("node") - if found_ds is not None: - da1, da2 = xr.align(ds[item], found_ds[item], join="inner") - if not np.allclose(da1.values, da2.values, equal_nan=True): - raise ValueError( - "Not all data in breakpoints are equivalent. " - "Select a specific node instead of the reach." - ) - else: - found_ds = ds - found_int_id = int_id - - if found_ds is not None and found_int_id is not None: - return NodeModelResult( - data=found_ds, - node=found_int_id, - name=self.name, - item=item, - quantity=self.quantity, - aux_items=self.sel_items.aux, + + # A breakpoint can name a quantity and hold nothing for it. + with_data = [ + (location, df) + for location, df in zip(found, self._read(found)) + if df[item].notna().any() + ] + if not with_data: + raise ValueError( + f"Reach '{reach_id}' has breakpoints that name quantity " + f"'{item}', but none of them carry values for it in this model " + "result." ) - if missing_node_data: + values = np.column_stack([df[item].to_numpy() for _, df in with_data]) + if not np.allclose(values, values[:, :1], equal_nan=True): raise ValueError( - f"Reach '{reach_id}' has breakpoint data for quantity " - f"'{item}', but matching breakpoint nodes are " - "missing from the model dataset. Re-create the NetworkModelResult " - "with the relevant reaches populated." + "Not all data in breakpoints are equivalent. " + "Select a specific node instead of the reach." ) - raise ValueError( - f"Reach '{reach_id}' was found in the network but none of its " - f"breakpoints have data loaded for quantity '{self.sel_items.values}'. " - f"Re-create the NetworkModelResult with the relevant reaches populated." + # Lowest position first, so the breakpoint chosen does not depend on the + # order the network happened to list them in. + location, df = min(with_data, key=lambda pair: pair[0].address[1]) + return self._to_result(location.address, df) + + def _read(self, found: Sequence[NetworkLocation]) -> list[pd.DataFrame]: + # One read for every location, each giving a frame of the selected items. + # An auxiliary item a location does not carry is missing there, as it + # would be anywhere else it is not measured. + keys = [ + (n, q) + for n, location in enumerate(found) + for q in self.sel_items.all + if q in location.quantities + ] + df = self.network.read([(found[n].address, q) for n, q in keys]) + df.columns = pd.MultiIndex.from_tuples(keys, names=[None, None]) + df = df.reindex( + columns=pd.MultiIndex.from_product([range(len(found)), self.sel_items.all]) + ).rename_axis("time") + return [df[n] for n in range(len(found))] + + def _to_result(self, address: Address, df: pd.DataFrame) -> NodeModelResult: + # The address is the network's own spelling of it rather than the + # observation's, so a position given as 24.5001 is recorded as 24.5. + item = self.sel_items.values + # MIKE 1D stores quantities at different grid points, so a breakpoint + # carrying Discharge may carry no WaterLevel; and a location can name a + # quantity and hold nothing for it. + if not df[item].notna().any(): + raise _no_data_error(address, item) + + if isinstance(address, tuple): + address = (str(address[0]), address[1]) + ds = _parse_point_input( + xr.Dataset.from_dataframe(df), + self.name, + item, + self.quantity, + self.sel_items.aux, + coords=NetworkCoords(address), ) - - def _resolve_alias(self, alias: int | str | tuple[str, float]) -> int: - # Resolve a node alias to an internal node ID. - - # Breakpoint tuple aliases are matched first by exact key lookup and then - # by reach ID and distance within ``_CHAINAGE_TOLERANCE``. If multiple - # candidates are within tolerance, the closest distance is selected; ties - # are broken by choosing the smallest node ID. Distance units are the - # same as the network chainage units. - - if isinstance(alias, int): - if alias not in self.data.node: - raise ValueError( - f"Node {alias} not found. Available: {list(self.nodes[:5])}..." - ) - return alias - else: - if alias in self.network._alias_map: - return self.network._alias_map[alias] - - if isinstance(alias, tuple): - # Handle tolerances - reach_id, distance = alias - candidates: list[tuple[float, int]] = [] - for key, node_id in self.network._alias_map.items(): - if isinstance(key, tuple) and key[0] == reach_id: - diff = abs(key[1] - distance) - if diff <= self._CHAINAGE_TOLERANCE: - candidates.append((diff, node_id)) - if candidates: - return min( - candidates, key=lambda candidate: (candidate[0], candidate[1]) - )[1] - - available = list(self.network._alias_map.keys())[:5] - if isinstance(alias, tuple): - raise ValueError( - f"Breakpoint {alias} not found in network. " - f"Available aliases (first 5): {available}" - ) - raise ValueError( - f"Node alias '{alias}' not found in network. " - f"Available aliases (first 5): {available}" - ) + return NodeModelResult(ds) diff --git a/src/modelskill/network.py b/src/modelskill/network.py deleted file mode 100644 index 4487c5809..000000000 --- a/src/modelskill/network.py +++ /dev/null @@ -1,1200 +0,0 @@ -"""Opt-in network module for network model results (e.g. MIKE 1D / res1d). - -Requires the ``networks`` dependency group (networkx, mikeio1d). -Install with:: - - uv sync --group networks - -Import this module explicitly to use network functionality:: - - from modelskill.network import Network - -""" - -from __future__ import annotations - -import sys - -from abc import ABC, abstractmethod -from pathlib import Path -from typing import Any, Sequence, overload, TYPE_CHECKING -from copy import deepcopy - -import networkx as nx -import pandas as pd -import xarray as xr - -if TYPE_CHECKING: - from mikeio1d import Res1D - from mikeio1d.result_network import ResultReach - from .model.adapters._res1d import Res1DReach - - -_MIKE_EXTENSIONS = frozenset({".res1d", ".res11"}) -_EPANET_EXTENSIONS = frozenset({".res"}) - -_NO_FIXTURE = ( - "{product} results are not supported yet: modelskill has no test fixture for " - "this format, so support cannot be verified. Please open an issue if you need it." -) -# A result file that holds timeseries but no topology of its own. The connectivity -# is in a companion file we do not parse yet. -_TOPOLOGY_IN_COMPANION_FILE = ( - "SWMM '.out' files carry no reach connectivity of their own - it lives in the " - "companion '.inp' input file, which modelskill does not read yet. Tracked in " - "https://github.com/DHI/modelskill/issues/689." -) -# A companion result file: readable, but it describes a network defined elsewhere. -_COMPANION_RESULT_FILE = ( - "'.resx' holds extra EPANET results (tank volume, pump energy) for a network " - "defined in the sibling '.res' file, so it has no topology of its own. Read the " - "'.res' file and pass this one alongside it: " - "Network.from_epanet(res, resx=...)." -) - -# extension -> why modelskill will not read it, even though mikeio1d can -_UNSUPPORTED_EXTENSIONS: dict[str, str] = { - ".out": _TOPOLOGY_IN_COMPANION_FILE, - ".resx": _COMPANION_RESULT_FILE, - ".prf": _NO_FIXTURE.format(product="MOUSE"), - ".crf": _NO_FIXTURE.format(product="MOUSE"), - ".xrf": _NO_FIXTURE.format(product="MOUSE"), - ".whr": _NO_FIXTURE.format(product="Water Hammer"), -} - -# extension -> the constructor that reads it, for "use X instead" errors -_EXTENSION_CONSTRUCTORS: dict[str, str] = { - **{extension: "from_mike" for extension in _MIKE_EXTENSIONS}, - **{extension: "from_epanet" for extension in _EPANET_EXTENSIONS}, -} - - -def _check_file_path_is_str(res: Res1D) -> None: - """Reject a Res1D opened with a path object rather than a string. - - mikeio1d resolves reach topology with ``str.endswith`` on - ``Res1D.file_path``, which raises ``AttributeError`` from deep inside the - load when that attribute is a ``Path``. Fail here instead, where the cause - can be named. - """ - file_path = getattr(res, "file_path", None) - if file_path is not None and not isinstance(file_path, str): - raise TypeError( - f"This Res1D was opened with a {type(file_path).__name__} file_path, " - "which mikeio1d cannot resolve reach topology from. Re-open it as " - "Res1D(str(path)), or pass the path to the constructor directly." - ) - - -class NetworkNode(ABC): - """Abstract base class for a node in a network. - - A node represents a discrete location in the network (e.g. a junction, - reservoir, or boundary point) that carries time-series data for one or - more physical quantities. - - Three properties must be implemented: - - * :attr:`id` - a unique string identifier for the node. - * :attr:`data` - a time-indexed :class:`pandas.DataFrame` whose columns - are quantity names. - * :attr:`boundary` - a dict of boundary-condition metadata (may be empty). - - The concrete helper :class:`BasicNode` is provided for the common case - where the data is already available as a DataFrame. - - See Also - -------- - BasicNode : Ready-to-use concrete implementation. - NetworkReach : Connects two NetworkNode instances. - Network : Container that assembles nodes and reaches into a graph. - """ - - @property - @abstractmethod - def id(self) -> str: - """Unique string identifier for this node.""" - pass - - @property - @abstractmethod - def data(self) -> pd.DataFrame: - """Time-indexed DataFrame with one column per quantity.""" - pass - - @property - @abstractmethod - def boundary(self) -> dict[str, Any]: - """Boundary-condition metadata dict (may be empty).""" - pass - - @property - def quantities(self) -> list[str]: - """List of quantity names available at this node.""" - return list(self.data.columns) - - -class ReachBreakPoint(ABC): - """Abstract base class for an intermediate break point along a network reach. - - Break points represent locations between the start and end nodes of a - reach (e.g. cross-section chainage points along a river reach) that carry - their own time-series data. - - Two properties must be implemented: - - * :attr:`id` - a ``(reach_id, distance)`` tuple that uniquely locates the - break point within the network. - * :attr:`data` - a time-indexed :class:`pandas.DataFrame` whose columns - are quantity names. - - The :attr:`distance` convenience property returns ``id[1]`` (the - along-reach distance in the units used by the parent network). - - Examples - -------- - Minimal subclass: - - >>> class MyBreakPoint(ReachBreakPoint): - ... def __init__(self, reach_id, chainage, df): - ... self._id = (reach_id, chainage) - ... self._data = df - ... @property - ... def id(self): return self._id - ... @property - ... def data(self): return self._data - - See Also - -------- - NetworkReach : Owns a list of ReachBreakPoint instances. - NetworkNode : Represents a start/end node of a reach. - Network : Assembles reaches (and their break points) into a graph. - """ - - @property - @abstractmethod - def id(self) -> tuple[str, float]: - """``(reach_id, distance)`` tuple uniquely identifying this break point.""" - pass - - @property - @abstractmethod - def data(self) -> pd.DataFrame: - """Time-indexed DataFrame with one column per quantity.""" - pass - - @property - def distance(self) -> float: - """Along-reach distance of this break point, measured from the start node.""" - return self.id[1] - - @property - def quantities(self) -> list[str]: - """List of quantity names available at this break point.""" - return list(self.data.columns) - - -class NetworkReach(ABC): - """Abstract base class for a reach in a network. - - A reach represents a directed connection between two :class:`NetworkNode` - instances (e.g. a river reach between two junctions). It may also carry - a list of :class:`ReachBreakPoint` objects for intermediate chainage - locations. - - Subclass this to integrate your own network topology. Four properties - must be implemented: - - * :attr:`id` - a unique string identifier for the reach. - * :attr:`start` - the upstream/start :class:`NetworkNode`. - * :attr:`end` - the downstream/end :class:`NetworkNode`. - * :attr:`breakpoints` - list of :class:`ReachBreakPoint` instances ordered - by increasing distance from the start node (empty list if none). - - :attr:`length` is optional and defaults to ``None``. Reach length matters - in some domains (rivers, sewer networks) and not in others (link-node water - distribution models), so override it only where a length exists. - - The concrete helper :class:`BasicReach` is provided for the common case - where all data is already available in memory. - - Examples - -------- - Minimal subclass, without a length: - - >>> class MyReach(NetworkReach): - ... def __init__(self, rid, start_node, end_node): - ... self._id = rid - ... self._start = start_node - ... self._end = end_node - ... @property - ... def id(self): return self._id - ... @property - ... def start(self): return self._start - ... @property - ... def end(self): return self._end - ... @property - ... def breakpoints(self): return [] - - Add a :attr:`length` property on top of that when the domain has one: - - >>> class MyMeasuredReach(MyReach): - ... def __init__(self, rid, start_node, end_node, length): - ... super().__init__(rid, start_node, end_node) - ... self._length = length - ... @property - ... def length(self): return self._length - - See Also - -------- - BasicReach : Ready-to-use concrete implementation. - NetworkNode : Represents the start/end of this reach. - ReachBreakPoint : Intermediate data points along this reach. - Network : Assembles a list of NetworkReach objects into a graph. - """ - - @property - @abstractmethod - def id(self) -> str: - """Unique string identifier for this reach.""" - pass - - @property - @abstractmethod - def start(self) -> NetworkNode: - """Start (upstream) node of this reach.""" - pass - - @property - @abstractmethod - def end(self) -> NetworkNode: - """End (downstream) node of this reach.""" - pass - - @property - def length(self) -> float | None: - """Total length of this reach in network units, or ``None`` if undefined.""" - return None - - @property - @abstractmethod - def breakpoints(self) -> list[ReachBreakPoint]: - """Ordered list of intermediate :class:`ReachBreakPoint` objects (may be empty).""" - pass - - @property - def n_breakpoints(self) -> int: - """Number of break points in the reach.""" - return len(self.breakpoints) - - -class BasicNode(NetworkNode): - """Concrete :class:`NetworkNode` for programmatic network construction. - - Parameters - ---------- - id : str - Unique node identifier. - data : pd.DataFrame - Time-indexed DataFrame with one column per quantity. - boundary : dict, optional - Boundary condition metadata, by default empty. - - Examples - -------- - >>> import pandas as pd - >>> time = pd.date_range("2020", periods=3, freq="h") - >>> node = BasicNode("junction_1", pd.DataFrame({"WaterLevel": [1.0, 1.1, 1.2]}, index=time)) - """ - - def __init__( - self, - id: str, - data: pd.DataFrame, - boundary: dict[str, Any] | None = None, - ) -> None: - self._id = id - self._data = data - self._boundary: dict[str, Any] = boundary or {} - - @property - def id(self) -> str: - return self._id - - @property - def data(self) -> pd.DataFrame: - return self._data - - @property - def boundary(self) -> dict[str, Any]: - return self._boundary - - -class BasicReach(NetworkReach): - """Concrete :class:`NetworkReach` for programmatic network construction. - - Parameters - ---------- - id : str - Unique reach identifier. - start : NetworkNode - Start node. - end : NetworkNode - End node. - length : float, optional - Reach length, by default None (undefined). - breakpoints : list[ReachBreakPoint], optional - Intermediate break points, by default empty. - - Examples - -------- - >>> reach = BasicReach("reach_1", node_a, node_b, length=250.0) - - Where the domain has no reach length, leave it out: - - >>> reach = BasicReach("pipe_1", node_a, node_b) - """ - - def __init__( - self, - id: str, - start: NetworkNode, - end: NetworkNode, - length: float | None = None, - breakpoints: list[ReachBreakPoint] | None = None, - ) -> None: - self._id = id - self._start = start - self._end = end - self._length = length - self._breakpoints: list[ReachBreakPoint] = breakpoints or [] - - @property - def id(self) -> str: - return self._id - - @property - def start(self) -> NetworkNode: - return self._start - - @property - def end(self) -> NetworkNode: - return self._end - - @property - def length(self) -> float | None: - return self._length - - @property - def breakpoints(self) -> list[ReachBreakPoint]: - return self._breakpoints - - -class Network: - """Network built from a set of reaches, with coordinate lookup and data access.""" - - def __init__(self, reaches: Sequence[NetworkReach]): - graph = self._generate_graph(reaches) - self._initialize_network_attributes(graph) - self._reaches = self._generate_reaches_dict(reaches) - - def _initialize_network_attributes(self, graph: nx.Graph): - self._alias_map = self._generate_alias_map(graph) - self._df = self._build_dataframe(graph) - self._graph = graph.copy() - - def __repr__(self) -> str: - time = self._df.index - time_window = "N/A - N/A" if len(time) == 0 else f"{time[0]} - {time[-1]}" - out = [ - "", - f"Reaches: {len(self._reaches)}", - f"Nodes: {self._graph.number_of_nodes()}", - f"Quantities: {self.quantities}", - f"Time: {time_window}", - ] - return "\n".join(out) - - @classmethod - def from_mike( - cls, - res: str | Path | Res1D, - *, - nodes: str | list[str] | None = None, - reaches: str | list[str] | None = None, - ) -> Network: - """Create a Network from a MIKE 1D or MIKE 11 result file. - - Parameters - ---------- - res : str, Path or Res1D - Path to a ``.res1d`` or ``.res11`` file, or an already-opened - :class:`mikeio1d.Res1D` object. - nodes : str, list of str, or None, optional - Controls which nodes have their timeseries data loaded into memory. - - * ``None`` *(default)* — data is loaded for every node. - * A single node ID or a list of node IDs — only those nodes get - data; others are topology-only. - * ``[]`` (empty list) — no node data is loaded at all. - - The full network topology is always constructed regardless of this - setting, so ``find()`` and ``recall()`` still work on all nodes. - reaches : str, list of str, or None, optional - Controls which reaches have their intermediate gridpoint data - populated. - - * ``None`` *(default)* — gridpoints are populated for every reach. - * A single reach name or a list of reach names — only those reaches - get gridpoint data; others are topology-only. - * ``[]`` (empty list) — no gridpoint data is loaded at all. - - Returns - ------- - Network - - Raises - ------ - NotImplementedError - If the file extension is not one modelskill can read. - ValueError - If the extension belongs to another constructor, such as EPANET. - - Examples - -------- - Load everything (default behaviour): - - >>> from modelskill.network import Network - >>> network = Network.from_mike("model.res1d") - - Load data only for the two nodes where observations exist, and skip - all intermediate gridpoint data to keep memory usage low: - - >>> network = Network.from_mike( - ... "model.res1d", - ... nodes=["node_a", "node_b"], - ... reaches=[], - ... ) - - Load data for selected nodes and gridpoints for one specific reach: - - >>> network = Network.from_mike( - ... "model.res1d", - ... nodes=["node_a", "node_b"], - ... reaches=["reach_1"], - ... ) - - Notes - ----- - MIKE 11 keeps its timeseries on reach gridpoints rather than on nodes, - so the nodes of a ``.res11`` network carry no data of their own. Pass - ``reaches`` rather than ``nodes`` to control what gets loaded. - - See Also - -------- - from_epanet : Read an EPANET result file. - """ - return cls._from_mikeio1d( - res, - nodes=nodes, - reaches=reaches, - allowed=_MIKE_EXTENSIONS, - caller="from_mike", - ) - - @classmethod - def from_epanet( - cls, - res: str | Path | Res1D, - *, - resx: str | Path | Res1D | None = None, - inp: str | Path | None = None, - nodes: str | list[str] | None = None, - reaches: str | list[str] | None = None, - ) -> Network: - """Create a Network from an EPANET result file and its companions. - - An EPANET run writes up to three files that modelskill can use. The - ``.res`` holds the network and its main timeseries; the optional - ``.resx`` holds extra results; and the optional ``.inp`` is the input - file, which is the only one of the three carrying reach lengths. - - Parameters - ---------- - res : str, Path or Res1D - Path to a ``.res`` file, or an already-opened - :class:`mikeio1d.Res1D` object. - resx : str, Path, Res1D or None, optional - Companion ``.resx`` file from the same run. Its extra node - quantities (tank ``Volume`` and ``Volume Percentage``) are merged - onto the matching nodes. By default None, and those quantities are - simply absent. - inp : str, Path or None, optional - EPANET ``.inp`` input file for the same model, read for its - ``[PIPES]`` lengths. By default None, and reach lengths are - undefined. - nodes : str, list of str, or None, optional - Which nodes get their timeseries loaded. See :meth:`from_mike`. - reaches : str, list of str, or None, optional - Which reaches get their gridpoint data loaded. See - :meth:`from_mike`. EPANET results have no intermediate gridpoints, - so this argument has no effect. - - Returns - ------- - Network - - Raises - ------ - NotImplementedError - If the file extension is not one modelskill can read. - ValueError - If the extension belongs to another constructor, such as MIKE, if a - companion file has the wrong extension, or if ``resx`` does not come - from the same run as ``res``. - - Examples - -------- - >>> from modelskill.network import Network - >>> network = Network.from_epanet("model.res") - - With both companions, for real edge lengths and the extra quantities: - - >>> network = Network.from_epanet( - ... "model.res", - ... resx="model.resx", - ... inp="model.inp", - ... ) - - Notes - ----- - EPANET is a link-node model, and mikeio1d reports no length and a - single synthetic gridpoint for each of its reaches. As a result: - - * without ``inp``, every edge of :attr:`graph` has ``length=None``, so a - length-weighted graph algorithm fails rather than returning a - meaningless number. Pumps and valves keep ``length=None`` even with - ``inp``, since ``[PIPES]`` is the only section carrying lengths - * reaches have no breakpoints, so - :class:`~modelskill.obs.ReachObservation` cannot be matched against - an EPANET network — use :class:`~modelskill.obs.NodeObservation` - * ``find(reach=..., distance=)`` never resolves; only - ``distance="start"`` and ``distance="end"`` work - - For the same reason, ``resx`` merges node quantities only. Its - reach-level quantities (pump energy, efficiency and costs) have no - breakpoint to live on, which is tracked in issue #680. - - Node timeseries, :meth:`to_dataframe`, :meth:`to_dataset`, - ``find(node=...)`` and :meth:`recall` are unaffected. - - See Also - -------- - from_mike : Read a MIKE 1D or MIKE 11 result file. - """ - return cls._from_mikeio1d( - res, - nodes=nodes, - reaches=reaches, - allowed=_EPANET_EXTENSIONS, - caller="from_epanet", - resx=resx, - inp=inp, - ) - - @classmethod - def _from_mikeio1d( - cls, - res: str | Path | Res1D, - *, - nodes: str | list[str] | None, - reaches: str | list[str] | None, - allowed: frozenset[str], - caller: str, - resx: str | Path | Res1D | None = None, - inp: str | Path | None = None, - ) -> Network: - """Shared implementation behind the public ``from_*`` constructors. - - Parameters - ---------- - allowed : frozenset of str - Extensions this constructor accepts. - caller : str - Name of the public method, used in error messages. - resx : str, Path, Res1D or None, optional - Companion result file whose node quantities are merged in. - inp : str, Path or None, optional - Companion input file read for reach lengths. - """ - if sys.version_info >= (3, 14): - raise NotImplementedError( - f"Current version of 'mikeio1d' requires python < 3.14 and {sys.version} is being used." - ) - - from mikeio1d import Res1D as _Res1D - - if isinstance(res, (str, Path)): - path = Path(res) - cls._validate_extension(path.suffix, allowed=allowed, caller=caller) - res = _Res1D(str(path)) - elif isinstance(res, _Res1D): - _check_file_path_is_str(res) - suffix = Path(res.file_path).suffix - cls._validate_extension(suffix, allowed=allowed, caller=caller) - else: - raise TypeError( - f"Expected a str, Path or Res1D object, got {type(res).__name__!r}" - ) - - if nodes is None: - nodes_list: list[str] = list(res.nodes.keys()) - elif isinstance(nodes, str): - nodes_list = [nodes] - else: - nodes_list = list(nodes) - - if reaches is None: - reaches_list: list[str] = list(res.reaches.keys()) - elif isinstance(reaches, str): - reaches_list = [reaches] - else: - reaches_list = list(reaches) - - extra = None if resx is None else cls._open_companion_result(res, resx) - lengths = None if inp is None else cls._read_companion_lengths(inp) - - list_of_reaches = cls._load_res1d_network( - res, nodes_list, reaches_list, extra=extra, lengths=lengths - ) - return cls(list_of_reaches) - - @staticmethod - def _read_companion_lengths(inp: str | Path) -> dict[str, float]: - """Read reach lengths from a companion ``.inp`` input file.""" - from modelskill.model.adapters._inp import read_pipe_lengths - - path = Path(inp) - if path.suffix.lower() != ".inp": - raise ValueError( - f"Expected an EPANET '.inp' input file, got '{path.suffix}'. " - "This argument reads reach lengths from the model input, not " - "from a result file." - ) - return read_pipe_lengths(path) - - @staticmethod - def _open_companion_result(res: Res1D, resx: str | Path | Res1D) -> Res1D: - """Open and validate a companion ``.resx`` result file. - - Raises - ------ - ValueError - If the extension is not ``.resx``, or if the file does not come from - the same run as ``res``. - """ - from mikeio1d import Res1D as _Res1D - - if isinstance(resx, (str, Path)): - path = Path(resx) - if path.suffix.lower() != ".resx": - raise ValueError( - f"Expected an EPANET '.resx' companion file, got '{path.suffix}'." - ) - extra = _Res1D(str(path)) - elif isinstance(resx, _Res1D): - _check_file_path_is_str(resx) - if Path(resx.file_path).suffix.lower() != ".resx": - raise ValueError( - "Expected an EPANET '.resx' companion file, got " - f"'{Path(resx.file_path).suffix}'." - ) - extra = resx - else: - raise TypeError( - f"Expected a str, Path or Res1D object, got {type(resx).__name__!r}" - ) - - # Merging two different runs would line up silently and produce a network - # that is wrong in a way no later error would reveal. - if not res.time_index.equals(extra.time_index): - raise ValueError( - "The '.resx' companion does not share a time axis with the " - "'.res' file, so the two are not from the same run. Got " - f"{len(extra.time_index)} steps ending {extra.end_time} against " - f"{len(res.time_index)} ending {res.end_time}." - ) - - unknown_nodes = set(extra.nodes) - set(res.nodes) - if unknown_nodes: - raise ValueError( - f"The '.resx' companion holds nodes {sorted(unknown_nodes)} that are " - "absent from the '.res' network, so the two files do not describe " - "the same model." - ) - - unknown_reaches = set(extra.reaches) - set(res.reaches) - if unknown_reaches: - raise ValueError( - f"The '.resx' companion holds reaches {sorted(unknown_reaches)} that are " - "absent from the '.res' network, so the two files do not describe " - "the same model." - ) - - return extra - - @staticmethod - def _validate_extension( - suffix: str, *, allowed: frozenset[str], caller: str - ) -> None: - """Check a file extension against mikeio1d and against one constructor. - - Raises - ------ - NotImplementedError - If modelskill cannot read the extension, either because mikeio1d - does not support it or because modelskill does not. - ValueError - If another constructor is the one that reads this extension. - """ - from mikeio1d import Res1D as _Res1D - - extension = suffix.lower() - - # Checked before the supported set below, since these all *are* readable - # by mikeio1d - it is modelskill that cannot use the result. - reason = _UNSUPPORTED_EXTENSIONS.get(extension) - if reason is not None: - raise NotImplementedError(f"Cannot read '{suffix}' files. {reason}") - - supported = _Res1D.get_supported_file_extensions() - if extension not in supported: - readable = sorted(supported - set(_UNSUPPORTED_EXTENSIONS)) - raise NotImplementedError( - f"Unsupported file extension '{suffix}'. " - f"Supported extensions are {readable}." - ) - - if extension not in allowed: - constructor = _EXTENSION_CONSTRUCTORS.get(extension) - if constructor is None: - raise NotImplementedError( - f"File extension '{suffix}' is supported by mikeio1d but is not mapped " - "to a Network constructor in this version of modelskill. " - "Please upgrade modelskill or open an issue." - ) - raise ValueError( - f"Network.{caller}() reads {sorted(allowed)} files, got '{suffix}'. " - f"Use Network.{constructor}() instead." - ) - - @staticmethod - def _load_res1d_network( - res: Res1D, - nodes: list[str], - reaches: list[str], - *, - extra: Res1D | None = None, - lengths: dict[str, float] | None = None, - ) -> list[Res1DReach]: - from modelskill.model.adapters._res1d import ( - Res1DReach, - Res1DNode, - _merge_extra_quantities, - _simplify_colnames, - ) - - nodes_set = set(nodes) - reaches_set = set(reaches) - lengths = lengths or {} - - # In order to work with bigger files, we might want to select a subset of nodes and avoid - # potential memory issues. For this reason, we create this intermediate step that populates - # only the data in the passed nodes - - def _init_node(reach: ResultReach, is_end: bool) -> Res1DNode: - id = reach.end_node if is_end else reach.start_node - gpt_idx = -1 if is_end else 0 - if id in nodes_set: - node = res.nodes[id] - df = _simplify_colnames(node) - # Merged here rather than up front so selective loading still - # decides what is held in memory. - if extra is not None and id in extra.nodes: - df = _merge_extra_quantities( - df, _simplify_colnames(extra.nodes[id]), node_id=id - ) - overlapping_gridpoint = reach.gridpoints[gpt_idx] - boundary = _simplify_colnames(overlapping_gridpoint) - return Res1DNode(id, data=df, boundary={reach.name: boundary}) - else: - return Res1DNode(id) - - return [ - Res1DReach( - reach, - _init_node(reach, False), - _init_node(reach, True), - populate_gridpoints=reach.name in reaches_set, - length=lengths.get(reach.name), - ) - for reach in res.reaches.values() - ] - - @staticmethod - def _generate_alias_map(g: nx.Graph) -> dict[str | tuple[str, float], int]: - return {g.nodes[id]["alias"]: id for id in g.nodes()} - - @staticmethod - def _generate_reaches_dict( - reaches: Sequence[NetworkReach], - ) -> dict[str, NetworkReach]: - return {r.id: r for r in reaches} - - @staticmethod - def _build_dataframe(g: nx.Graph) -> pd.DataFrame: - data_in_nodes = { - k: v["data"] - for k, v in g.nodes.items() - if v["data"] is not None and not v["data"].empty - } - if len(data_in_nodes) == 0: - columns = pd.MultiIndex.from_arrays([[], []], names=["node", "quantity"]) - return pd.DataFrame(index=pd.Index([], name="time"), columns=columns) - df = pd.concat(data_in_nodes, axis=1) - df.columns = df.columns.set_names(["node", "quantity"]) - df.index.name = "time" - return df.copy() - - def to_dataframe(self, sel: str | None = None) -> pd.DataFrame: - """Dataframe using node ids as column names. - - It will be multiindex unless 'sel' is passed. - - Parameters - ---------- - sel : Optional[str], optional - Quantity to select, by default None - - Returns - ------- - pd.DataFrame - Timeseries contained in graph nodes - """ - df = self._df.copy() - if sel is None: - return df - else: - df.attrs["quantity"] = sel - return df.reorder_levels(["quantity", "node"], axis=1).loc[:, sel] - - def to_dataset(self) -> xr.Dataset: - """Dataset using node ids as coords. - - Returns - ------- - xr.Dataset - Timeseries contained in graph nodes - """ - df_raw = self.to_dataframe() - if len(df_raw.columns) == 0: - return xr.Dataset() - df = df_raw.reorder_levels(["quantity", "node"], axis=1) - quantities = df.columns.get_level_values("quantity").unique() - return xr.Dataset( - {q: xr.DataArray(df[q], dims=["time", "node"]) for q in quantities} - ) - - @property - def graph(self) -> nx.Graph: - """Graph of the network.""" - return self._graph - - @property - def quantities(self) -> list[str]: - """Quantities present in data. - - Returns - ------- - List[str] - List of quantities - """ - return list(self.to_dataframe().columns.get_level_values(1).unique()) - - @staticmethod - def _generate_graph(reaches: Sequence[NetworkReach]) -> nx.Graph: - g0 = nx.Graph() - for reach in reaches: - # 1) Add start and end nodes - for node in [reach.start, reach.end]: - node_key = node.id - if node_key in g0.nodes: - g0.nodes[node_key]["boundary"].update(node.boundary) - else: - g0.add_node(node_key, data=node.data, boundary=node.boundary) - - # 2) Add edges connecting start/end nodes to their adjacent breakpoints - start_key = reach.start.id - end_key = reach.end.id - if reach.n_breakpoints == 0: - g0.add_edge(start_key, end_key, length=reach.length) - else: - bp_keys = [bp.id for bp in reach.breakpoints] - for bp, bp_key in zip(reach.breakpoints, bp_keys): - g0.add_node(bp_key, data=bp.data) - - g0.add_edge(start_key, bp_keys[0], length=reach.breakpoints[0].distance) - - # Only the final segment needs the total length. Break point - # distances are known even when the total is not, so a reach - # without a length still gets real lengths on every edge but - # this one. - tail_length = ( - None - if reach.length is None - else reach.length - reach.breakpoints[-1].distance - ) - g0.add_edge(bp_keys[-1], end_key, length=tail_length) - - # 3) Connect consecutive intermediate breakpoints - for i in range(reach.n_breakpoints - 1): - current_ = reach.breakpoints[i] - next_ = reach.breakpoints[i + 1] - length = next_.distance - current_.distance - g0.add_edge( - current_.id, - next_.id, - length=length, - ) - - return nx.convert_node_labels_to_integers(g0, label_attribute="alias") - - @overload - def find( - self, - *, - node: str, - reach: None = None, - distance: None = None, - ) -> int: - pass - - @overload - def find( - self, - *, - node: list[str], - reach: None = None, - distance: None = None, - ) -> list[int]: - pass - - @overload - def find( - self, - *, - node: None = None, - reach: str | list[str], - distance: str | float, - ) -> int: - pass - - @overload - def find( - self, - *, - node: None = None, - reach: str | list[str], - distance: list[str | float], - ) -> list[int]: - pass - - def find( - self, - node: str | list[str] | None = None, - reach: str | list[str] | None = None, - distance: str | float | list[str | float] | None = None, - ) -> int | list[int]: - """Find node or breakpoint id in the Network object based on former coordinates. - - Parameters - ---------- - node : str | List[str], optional - Node id(s) in the original network, by default None - reach : str | List[str], optional - Reach id(s) for breakpoint lookup or reach endpoint lookup, by default None - distance : str | float | List[str | float], optional - Distance(s) along reach for breakpoint lookup, or "start"/"end" - for reach endpoints, by default None - - Returns - ------- - int | List[int] - Node or breakpoint id(s) in the generic network - - Raises - ------ - ValueError - If invalid combination of parameters is provided - KeyError - If requested node/breakpoint is not found in the network - """ - by_node = node is not None - by_breakpoint = reach is not None or distance is not None - - if by_node and by_breakpoint: - raise ValueError( - "Cannot specify both 'node' and 'reach'/'distance' parameters simultaneously" - ) - - if not by_node and not by_breakpoint: - raise ValueError( - "Must specify either 'node' or both 'reach' and 'distance' parameters" - ) - - ids: list[str | tuple[str, float]] - - if by_node: - assert node is not None - if not isinstance(node, list): - node = [node] - ids = list(node) - - else: - if reach is None or distance is None: - raise ValueError( - "Both 'reach' and 'distance' parameters are required for breakpoint/endpoint lookup" - ) - - if not isinstance(reach, list): - reach = [reach] - - if not isinstance(distance, list): - distance = [distance] - - if len(reach) == 1: - reach = reach * len(distance) - - if len(reach) != len(distance): - raise ValueError( - "Incompatible lengths of 'reach' and 'distance' arguments. One 'reach' admits multiple distances, otherwise they must be the same length." - ) - - ids = [] - for reach_i, distance_i in zip(reach, distance): - if distance_i in ["start", "end"]: - if reach_i not in self._reaches: - raise KeyError(f"Reach '{reach_i}' not found in the network.") - - network_reach = self._reaches[reach_i] - if distance_i == "start": - ids.append(network_reach.start.id) - else: - ids.append(network_reach.end.id) - else: - if not isinstance(distance_i, (int, float)): - raise ValueError( - "Invalid 'distance' value for breakpoint lookup: " - f"{distance_i!r}. Expected a numeric value or 'start'/'end'." - ) - ids.append((reach_i, distance_i)) - - _CHAINAGE_TOLERANCE = 1e-3 - - def _resolve_id(id): - if id in self._alias_map: - return self._alias_map[id] - if isinstance(id, tuple): - reach_id, distance = id - for key, val in self._alias_map.items(): - if ( - isinstance(key, tuple) - and key[0] == reach_id - and abs(key[1] - distance) <= _CHAINAGE_TOLERANCE - ): - return val - return None - - resolved = [_resolve_id(id) for id in ids] - missing_ids = [ids[i] for i, v in enumerate(resolved) if v is None] - if missing_ids: - raise KeyError( - f"Node/breakpoint(s) {missing_ids} not found in the network. Available nodes are {set(self._alias_map.keys())}" - ) - if len(resolved) == 1: - return resolved[0] - return resolved - - @overload - def recall(self, id: int) -> dict[str, Any]: - pass - - @overload - def recall(self, id: list[int]) -> list[dict[str, Any]]: - pass - - def recall(self, id: int | list[int]) -> dict[str, Any] | list[dict[str, Any]]: - """Recover the original coordinates of an element given the node id(s) in the Network object. - - Parameters - ---------- - id : int | List[int] - Node id(s) in the generic network - - Returns - ------- - Dict[str, Any] | List[Dict[str, Any]] - Original coordinates. For single input returns dict, for multiple inputs returns list of dicts. - Dict contains coordinates: - - For nodes: 'node' key with node id - - For breakpoints: 'reach' and 'distance' keys with reach id and distance - - Raises - ------ - KeyError - If node id is not found in the network - ValueError - If node id string format is invalid - """ - if not isinstance(id, list): - id = [id] - - reverse_alias_map = {v: k for k, v in self._alias_map.items()} - - results: list[dict[str, Any]] = [] - for node_id in id: - if node_id not in reverse_alias_map: - raise KeyError(f"Node ID {node_id} not found in the network.") - - key = reverse_alias_map[node_id] - if isinstance(key, str): - results.append({"node": key}) - else: - results.append({"reach": key[0], "distance": key[1]}) - - if len(results) == 1: - return results[0] - else: - return results - - def copy(self) -> "Network": - """Create a deep copy of the Network. - - Returns - ------- - Network - Deep copy of the Network object - """ - return deepcopy(self) - - -def _make_basic_network(node_ids, time, data, quantity="WaterLevel"): - nodes = [ - BasicNode(nid, pd.DataFrame({quantity: data[:, i]}, index=time)) - for i, nid in enumerate(node_ids) - ] - reaches = [ - BasicReach(f"r{i}", nodes[i], nodes[i + 1], length=100.0) - for i in range(len(nodes) - 1) - ] - return Network(reaches) diff --git a/src/modelskill/obs.py b/src/modelskill/obs.py index fa2cc01ca..521d390b3 100644 --- a/src/modelskill/obs.py +++ b/src/modelskill/obs.py @@ -1,21 +1,28 @@ """ # Observations -ModelSkill supports four types of observations: +ModelSkill supports five types of observations: * [`PointObservation`](`modelskill.PointObservation`) - a point timeseries from a dfs0/nc file or a DataFrame * [`TrackObservation`](`modelskill.TrackObservation`) - a track (moving point) timeseries from a dfs0/nc file or a DataFrame * [`VerticalObservation`](`modelskill.VerticalObservation`) - a vertical profile from a dfs0/nc file or a DataFrame -* [`NodeObservation`](`modelskill.NodeObservation`) - a network node timeseries for specific node IDs. +* [`NodeObservation`](`modelskill.NodeObservation`) - a network node timeseries for a named node or break point. +* [`ReachObservation`](`modelskill.ReachObservation`) - a network reach timeseries for a quantity uniform along the reach. An observation can be created by explicitly invoking one of the above classes or using the [`observation()`](`modelskill.observation`) function which will return the appropriate type based on the input data (if possible). """ from __future__ import annotations -from typing import Literal, Any, Union, overload +from typing import ( + Any, + Literal, + Union, + overload, +) from typing_extensions import Self import warnings +import numpy as np import pandas as pd import xarray as xr @@ -26,14 +33,22 @@ _parse_xyz_point_input, _parse_track_input, _parse_vertical_input, - _parse_network_node_input, - _parse_network_breakpoint_input, + _parse_point_input, +) +from .timeseries._coords import ( + NetworkCoords, + _reject_conflicting_location, + network_location, ) # NetCDF attributes can only be str, int, float https://unidata.github.io/netcdf4-python/#attributes-in-a-netcdf-file Serializable = Union[str, int, float] +# Where a node observation sits: the name the network gave the node, or a +# breakpoint given as (reach_id, position) along a reach. +NodeLocation = Union[str, tuple[str, float]] + def observation( data: DataInputType, @@ -78,7 +93,7 @@ def observation( >>> import modelskill as ms >>> o_pt = ms.observation(df, item=0, x=366844, y=6154291, name="Klagshamn") >>> o_tr = ms.observation("lon_after_lat.dfs0", item="wl", x_item=1, y_item=0) - >>> o_node = ms.observation(df, item="Water Level", at=123, name="123") + >>> o_node = ms.observation(df, item="Water Level", at="123", name="123") >>> o_reach = ms.observation(df, item="Discharge", reach="reach_1", name="reach_1_Q") """ if gtype is None: @@ -477,24 +492,37 @@ def z(self): return self._coordinate_values("z") +def _at_from_coords(ds: xr.Dataset) -> str | tuple[str, float]: + """The location in the form ``NodeObservation`` takes it. + + A node name, a ``(reach_id, position)`` breakpoint, or the reach name for + data that holds for a whole reach. Unlike :func:`~modelskill.timeseries._coords.network_location`, which + reports the location as recorded, this coerces to the types the ``at`` + argument is declared with. + """ + location = network_location(ds) + if isinstance(location, tuple): + return (str(location[0]), float(location[1])) + return str(location) + + class NodeObservation(Observation): """Class for observations at network nodes. Create a NodeObservation from a DataFrame or other data source. - The ``at`` parameter accepts three forms: + The ``at`` parameter accepts two forms: + + * **str** — the node's name in the model (e.g. a Res1D node name). + * **tuple[str, float]** — a breakpoint, as ``(reach_id, position)`` along a + reach. - * **int** — internal network ID, used directly. - * **str** — original node alias (e.g. Res1D node name), resolved to an - integer ID automatically when matched against a - :class:`~modelskill.model.network.NetworkModelResult`. - * **tuple[str, float]** — breakpoint location as ``(reach_id, distance)`` - along a reach, resolved via the alias map at match time. + Both are resolved against the network when the observation is matched. .. note:: "Node" in this API follows the broad graph sense: it covers both junctions (named connection points) and chainage points (breakpoints along a reach). MIKE 1D users who distinguish *node* (junction) from - *gridpoint* sharply can use the ``(reach_id, distance)`` tuple form to + *gridpoint* sharply can use the ``(reach_id, position)`` tuple form to target a specific chainage point, or :class:`ReachObservation` when the quantity is uniform across the whole reach and any breakpoint will do. @@ -505,12 +533,11 @@ class NodeObservation(Observation): ---------- data : str, Path, mikeio.Dataset, mikeio.DataArray, pd.DataFrame, pd.Series, xr.Dataset or xr.DataArray data source with time series for the node - at : int, str, or tuple[str, float] + at : str or tuple[str, float] Observation location. Accepted forms: - * **int** — internal network ID. - * **str** — original node alias (e.g. Res1D node name). - * **tuple[str, float]** — breakpoint as ``(reach_id, distance)``. + * **str** — the node's name in the model (e.g. a Res1D node name). + * **tuple[str, float]** — a breakpoint, as ``(reach_id, position)``. item : (int, str), optional index or name of the wanted item/column, by default None if data contains more than one item, item must be given @@ -524,81 +551,93 @@ class NodeObservation(Observation): list of names or indices of auxiliary items, by default None attrs : dict, optional additional attributes to be added to the data, by default None + position_tol : float, optional + How far the position in ``at`` may be from a model breakpoint and + still match it. The nearest breakpoint inside it that carries the + model result's item is used. By default None, which absorbs only + rounding (mikeio1d's 1e-3). Ignored for a node name. + + Raises + ------ + ValueError + If ``position_tol`` is negative or not finite. Examples -------- >>> import modelskill as ms - >>> o1 = ms.NodeObservation(data, at=123, name="123") - >>> o2 = ms.NodeObservation(df, item="Water Level", at=456) + >>> o1 = ms.NodeObservation(data, at="123", name="123") + >>> o2 = ms.NodeObservation(df, item="Water Level", at="456") >>> >>> # String alias resolved at match time >>> o3 = ms.NodeObservation(data, at="node_A") >>> - >>> # Breakpoint as (reach_id, distance) tuple + >>> # Breakpoint as (reach_id, position) tuple >>> o4 = ms.NodeObservation(data, at=("reach_1", 24.5)) >>> + >>> # A measured chainage, snapped onto the nearest breakpoint within 0.5 + >>> o5 = ms.NodeObservation(data, at=("reach_1", 24.0), position_tol=0.5) + >>> >>> # Multiple node observations from separate data sources - >>> obs = ms.NodeObservation.from_multiple(nodes={123: df1, 456: df2}) + >>> obs = ms.NodeObservation.from_multiple(nodes={"123": df1, "456": df2}) """ def __init__( self, data: PointType, *, - at: int | str | tuple[str, float], + at: str | tuple[str, float], item: int | str | None = None, name: str | None = None, weight: float = 1.0, quantity: Quantity | None = None, aux_items: list[int | str] | None = None, attrs: dict | None = None, + position_tol: float | None = None, ) -> None: - if isinstance(at, tuple): - reach, distance = str(at[0]), float(at[1]) - if not self._is_input_validated(data): - data = _parse_network_breakpoint_input( - data, - name=name, - item=item, - quantity=quantity, - aux_items=aux_items, - reach=reach, - distance=distance, - ) + if position_tol is not None and not ( + np.isfinite(position_tol) and position_tol >= 0 + ): + raise ValueError( + f"position_tol must be a finite, non-negative number, got {position_tol!r}" + ) + if isinstance(at, (int, np.integer)) and not isinstance(at, bool): + raise TypeError( + "'at' takes a node name or a (reach, position) pair, not an integer. " + "The integers a Network hands out are an internal index; " + 'network.to_networkx().nodes[]["address"] gives the name back.' + ) + location: str | tuple[str, float] = ( + (str(at[0]), float(at[1])) if isinstance(at, tuple) else at + ) + if self._is_input_validated(data): + assert isinstance(data, xr.Dataset) + _reject_conflicting_location(data, location, argument="at") else: - if not self._is_input_validated(data): - data = _parse_network_node_input( - data, - name=name, - item=item, - quantity=quantity, - node=at, - aux_items=aux_items, - ) + data = _parse_point_input( + data, name, item, quantity, aux_items, coords=NetworkCoords(location) + ) assert isinstance(data, xr.Dataset) super().__init__(data=data, weight=weight, attrs=attrs) + self.position_tol = position_tol @property - def at(self) -> int | str | tuple[str, float]: - """Observation location: node ID (int/str) or breakpoint ``(reach_id, distance)`` tuple.""" - if "reach" in self.data.coords: - return ( - str(self.data.coords["reach"].item()), - float(self.data.coords["distance"].item()), - ) - return self.data.coords["node"].item() # int or str + def at(self) -> str | tuple[str, float]: + """Observation location: a node name, or a ``(reach_id, position)`` breakpoint.""" + return _at_from_coords(self.data) + + @property + def node(self) -> Any: + """Name of the node this observation sits at, or None for a break point.""" + return self._coordinate_values("node") + + def _location_repr(self) -> str | None: + return f"Location: {self.at}" def _create_new_instance(self, data: xr.Dataset) -> Self: """Reconstruct instance from a dataset slice.""" - if "reach" in data.coords: - return self.__class__( - data, - at=( - str(data.coords["reach"].item()), - float(data.coords["distance"].item()), - ), - ) - return self.__class__(data, at=data.coords["node"].item()) + return self.__class__( + data, at=_at_from_coords(data), position_tol=self.position_tol + ) @overload @classmethod @@ -606,10 +645,11 @@ def from_multiple( cls, *, data: PointType, - nodes: dict[int, str | int], + nodes: dict[NodeLocation, str | int], quantity: Quantity | None = None, aux_items: list[int | str] | None = None, attrs: dict | None = None, + position_tol: float | None = None, ) -> list[NodeObservation]: ... @overload @@ -617,10 +657,11 @@ def from_multiple( def from_multiple( cls, *, - nodes: dict[int, PointType], + nodes: dict[NodeLocation, PointType], quantity: Quantity | None = None, aux_items: list[int | str] | None = None, attrs: dict | None = None, + position_tol: float | None = None, ) -> list[NodeObservation]: pass @@ -629,10 +670,11 @@ def from_multiple( cls, *, data: PointType | None = None, - nodes: dict[int, Any] | None = None, + nodes: dict[NodeLocation, Any] | None = None, quantity: Quantity | None = None, aux_items: list[int | str] | None = None, attrs: dict | None = None, + position_tol: float | None = None, ) -> list[NodeObservation]: """Create multiple NodeObservation objects. @@ -641,31 +683,45 @@ def from_multiple( 1. **Separate data sources** — pass only ``nodes`` as a dict mapping each node ID to its own data source (file path, DataFrame, etc.):: - obs = NodeObservation.from_multiple(nodes={123: df1, 456: "sensor.csv"}) + obs = NodeObservation.from_multiple(nodes={"123": df1, "456": "sensor.csv"}) 2. **Shared data source** — pass a single ``data`` object together with ``nodes`` as a dict mapping each node ID to the column name or index to select from ``data``:: - obs = NodeObservation.from_multiple(data=df, nodes={123: "col_a", 456: "col_b"}) + obs = NodeObservation.from_multiple(data=df, nodes={"123": "col_a", "456": "col_b"}) Parameters ---------- data : PointType, optional - Shared data source (required when ``nodes`` values are column selectors). - nodes : dict[int, PointType | str | int] - Mapping of node_id -> data source or column selector. - quantity : Quantity | None, optional + Shared data source (required when ``nodes`` values are column + selectors). + nodes : dict[str | tuple[str, float], PointType | str | int] + Mapping of location -> data source or column selector. A location + takes either of the forms accepted by ``at``: a node name, or a + ``(reach_id, position)`` breakpoint. + + Note that a location can appear only once, so this form cannot + express several observations at the same node. + quantity : Quantity, optional Physical quantity metadata, by default None. aux_items : list[int | str] | None, optional Auxiliary items, by default None. attrs : dict | None, optional Additional attributes, by default None. + position_tol : float, optional + Tolerance for every breakpoint position, as in ``NodeObservation``, + by default None. Returns ------- list[NodeObservation] List of NodeObservation objects. + + Raises + ------ + ValueError + If ``nodes`` is not given. """ if nodes is None: raise ValueError("'nodes' argument is required") @@ -686,6 +742,7 @@ def from_multiple( quantity=quantity, aux_items=aux_items, attrs=attrs, + position_tol=position_tol, ) for data_i, node_i in zip(data_sources, node_ids) ] @@ -699,6 +756,7 @@ def from_multiple( quantity=quantity, aux_items=aux_items, attrs=attrs, + position_tol=position_tol, ) for node_i, item_i in zip(node_ids, node_items) ] @@ -712,7 +770,10 @@ class ReachObservation(Observation): nodes/breakpoints. A ReachObservation associates a timeseries with a named reach; when matched against a :class:`~modelskill.model.network.NetworkModelResult` the data is - extracted from an arbitrary breakpoint that belongs to that reach. + extracted from the breakpoints on that reach that carry the quantity + and hold data for it. When there are several, they must agree, and + the one with the lowest position is used. The comparer sits on the + reach; ``mr.extract(obs).at`` gives the breakpoint read. Parameters ---------- @@ -753,28 +814,159 @@ def __init__( aux_items: list[int | str] | None = None, attrs: dict | None = None, ) -> None: - if not self._is_input_validated(data): - data = _parse_network_breakpoint_input( + if self._is_input_validated(data): + assert isinstance(data, xr.Dataset) + carried = network_location(data) + if isinstance(carried, tuple): + raise ValueError( + f"The data sits at break point {carried!r}, a specific " + "chainage rather than the whole reach. Use " + f"NodeObservation(data, at={carried!r}) for a break point." + ) + _reject_conflicting_location(data, reach, argument="reach") + else: + # No position: the observation holds for the whole reach. + data = _parse_point_input( data, - name=name, - item=item, - quantity=quantity, - aux_items=aux_items, - reach=reach, - distance=None, + name, + item, + quantity, + aux_items, + coords=NetworkCoords((reach, None)), ) assert isinstance(data, xr.Dataset) super().__init__(data=data, weight=weight, attrs=attrs) + @property + def at(self) -> str: + """Observation location: the reach name.""" + return str(_at_from_coords(self.data)) + @property def reach(self) -> str: """Reach ID of this observation.""" return str(self.data.coords["reach"].item()) + def _location_repr(self) -> str | None: + return f"Location: {self.reach}" + def _create_new_instance(self, data: xr.Dataset) -> Self: """Reconstruct instance from a dataset slice.""" return self.__class__(data, reach=str(data.coords["reach"].item())) + @overload + @classmethod + def from_multiple( + cls, + *, + data: PointType, + reaches: dict[str, str | int], + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, + ) -> list[ReachObservation]: + pass + + @overload + @classmethod + def from_multiple( + cls, + *, + reaches: dict[str, PointType], + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, + ) -> list[ReachObservation]: + pass + + @classmethod + def from_multiple( + cls, + *, + data: PointType | None = None, + reaches: dict[str, Any] | None = None, + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, + ) -> list[ReachObservation]: + """Create multiple ReachObservation objects. + + Two calling conventions are supported: + + 1. **Separate data sources** — pass only ``reaches`` as a dict mapping + each reach ID to its own data source (file path, DataFrame, etc.):: + + obs = ReachObservation.from_multiple(reaches={"r1": df1, "r2": "sensor.csv"}) + + 2. **Shared data source** — pass a single ``data`` object together with + ``reaches`` as a dict mapping each reach ID to the column name or + index to select from ``data``:: + + obs = ReachObservation.from_multiple(data=df, reaches={"r1": "col_a", "r2": "col_b"}) + + Parameters + ---------- + data : PointType, optional + Shared data source (required when ``reaches`` values are column + selectors). + reaches : dict[str, PointType | str | int] + Mapping of reach_id -> data source or column selector. + + Note that a reach can appear only once, so this form cannot express + several observations on the same reach. + quantity : Quantity, optional + Physical quantity metadata, by default None. + aux_items : list[int | str] | None, optional + Auxiliary items, by default None. + attrs : dict | None, optional + Additional attributes, by default None. + + Returns + ------- + list[ReachObservation] + List of ReachObservation objects. + + Raises + ------ + ValueError + If ``reaches`` is not given. + """ + if reaches is None: + raise ValueError("'reaches' argument is required") + if not isinstance(reaches, dict): + raise TypeError( + f"'reaches' must be a dict mapping reach_id -> data_source, got {type(reaches).__name__}" + ) + + reach_ids = list(reaches.keys()) + + if data is None: + data_sources: list[PointType] = list(reaches.values()) + return [ + cls( + data_i, + reach=reach_i, + item=None, + quantity=quantity, + aux_items=aux_items, + attrs=attrs, + ) + for data_i, reach_i in zip(data_sources, reach_ids) + ] + else: + reach_items: list[int | str | None] = list(reaches.values()) + return [ + cls( + data, + reach=reach_i, + item=item_i, + quantity=quantity, + aux_items=aux_items, + attrs=attrs, + ) + for reach_i, item_i in zip(reach_ids, reach_items) + ] + def unit_display_name(name: str) -> str: """Display name diff --git a/src/modelskill/quantity.py b/src/modelskill/quantity.py index 99d2eb4c2..93701c4de 100644 --- a/src/modelskill/quantity.py +++ b/src/modelskill/quantity.py @@ -47,6 +47,10 @@ class Quantity: is_directional: bool = False def __str__(self): + if not self.name and not self.unit: + return "Undefined" + if not self.unit: + return self.name return f"{self.name} [{self.unit}]" def __repr__(self): diff --git a/src/modelskill/timeseries/__init__.py b/src/modelskill/timeseries/__init__.py index f52f17271..eb643aa00 100644 --- a/src/modelskill/timeseries/__init__.py +++ b/src/modelskill/timeseries/__init__.py @@ -1,9 +1,5 @@ from ._timeseries import TimeSeries -from ._point import ( - _parse_xyz_point_input, - _parse_network_node_input, - _parse_network_breakpoint_input, -) +from ._point import _parse_point_input, _parse_xyz_point_input from ._track import _parse_track_input from ._vertical import _parse_vertical_input @@ -12,6 +8,5 @@ "_parse_xyz_point_input", "_parse_track_input", "_parse_vertical_input", - "_parse_network_node_input", - "_parse_network_breakpoint_input", + "_parse_point_input", ] diff --git a/src/modelskill/timeseries/_coords.py b/src/modelskill/timeseries/_coords.py index 98304befa..640e0dc2f 100644 --- a/src/modelskill/timeseries/_coords.py +++ b/src/modelskill/timeseries/_coords.py @@ -1,4 +1,10 @@ +from __future__ import annotations + +import math +from typing import Any + import numpy as np +import xarray as xr class XYZCoords: @@ -17,35 +23,132 @@ def as_dict(self) -> dict: return {"x": self.x, "y": self.y, "z": self.z} -class NodeCoords: - def __init__(self, node: int | str | None = None): - self.node = node if node is not None else np.nan +class NetworkCoords: + """Coordinates for data at a location in a network. + + Parameters + ---------- + location : str or tuple of (str, float or None) + Where the data sits: a node name, a break point as + ``(reach, position)``, or a whole reach as ``(reach, None)``. A whole + reach stores no ``position`` coordinate, so the data can be matched to + any break point on the reach. Values are stored as given. + + Raises + ------ + ValueError + If ``location`` is None. + """ + + def __init__(self, location: str | tuple[str, float | None]): + if location is None: + raise ValueError("'location' argument cannot be empty.") + self.location = location @property def as_dict(self) -> dict: - return {"node": self.node} + if not isinstance(self.location, tuple): + return {"node": self.location} + reach, position = self.location + if position is None: + return {"reach": reach} + return {"reach": reach, "position": position} + + +def _coordinate_values(ds: xr.Dataset, coord: str) -> Any: + """A dataset's values for one coordinate, or None when it has no such coordinate. + + A scalar coordinate is unwrapped to its single value; anything else is + handed back as the array it is. + """ + if coord not in ds.coords: + return None + vals = ds[coord].values + return np.atleast_1d(vals)[0] if vals.ndim == 0 else vals + + +#: Scalar coordinates that say where a network timeseries sits, rather than what +#: it holds. They are dropped on the way to a dataframe, where they would +#: otherwise become columns. +NETWORK_LOCATION_COORDS = ("node", "reach", "position") -class ReachCoords: - """Coordinates for an observation along a network reach. +def network_location(ds: xr.Dataset) -> Any: + """Where a network timeseries sits, as the network that produced it named it. + + Returns a node name for a node, a ``(reach, position)`` pair for a + breakpoint, a reach name when no position was given, and None for data that + carries no network location. The value is returned as recorded. + """ + if "node" in ds.coords: + return _network_scalar(ds, "node") + if "reach" in ds.coords: + reach = _network_scalar(ds, "reach") + if "position" not in ds.coords: + return reach + return (reach, _network_scalar(ds, "position")) + return None + + +def _network_scalar(ds: xr.Dataset, name: str) -> Any: + value = _coordinate_values(ds, name) + return value.item() if hasattr(value, "item") else value + + +def _reject_conflicting_location(ds: xr.Dataset, named: Any, *, argument: str) -> None: + """Raise when data that already knows where it sits is given another location. + + A dataset that has been through modelskill carries its own location + coordinates, and the constructors cannot re-apply them, so a location named + alongside it would be dropped without a word. + + The comparison is a plain one rather than a lookup: an observation has no + network when it is built, so it cannot snap a near-miss position the way + :meth:`~modelskill.model.network.NetworkModelResult.extract` does. Parameters ---------- - reach : str - Reach identifier. - distance : float or None, optional - Along-reach distance (chainage). When ``None`` the observation is - reach-level (no specific chainage) and no ``distance`` coordinate is - stored in the dataset. + ds : xr.Dataset + Data that has already been through modelskill, and so carries its own + location coordinates. + named : str or tuple of (str, float) + The location the caller named: a node name, a reach name, or a + ``(reach, position)`` break point. + argument : str + Name of the keyword the location came from, for the error message. + + Raises + ------ + ValueError + If the data carries no network location, or carries a different one. """ + carried = network_location(ds) + if carried is None: + raise ValueError( + f"The data has been through modelskill but carries no network " + f"location, so {argument!r} ({named!r}) has nothing to agree with. " + "Build the observation from a DataFrame instead." + ) + if not _same_location(carried, named): + raise ValueError( + f"The data already sits at {carried!r}, but {argument!r} says " + f"{named!r}. A dataset that has been through modelskill carries its " + f"own location: pass {argument}={carried!r}, or build the observation " + "from a DataFrame to place it somewhere else." + ) - def __init__(self, reach: str, distance: float | None = None): - self.reach = reach - self.distance = distance - @property - def as_dict(self) -> dict: - d: dict = {"reach": self.reach} - if self.distance is not None: - d["distance"] = self.distance - return d +def _same_location(carried: Any, named: Any) -> bool: + """Whether two network locations name the same place. + + Break point positions are compared closely rather than exactly, so that a + location read off a dataset and handed straight back still agrees with + itself. + """ + if isinstance(carried, tuple) != isinstance(named, tuple): + return False + if isinstance(carried, tuple): + return str(carried[0]) == str(named[0]) and math.isclose( + float(carried[1]), float(named[1]) + ) + return str(carried) == str(named) diff --git a/src/modelskill/timeseries/_point.py b/src/modelskill/timeseries/_point.py index f0742f4dc..8727f8669 100644 --- a/src/modelskill/timeseries/_point.py +++ b/src/modelskill/timeseries/_point.py @@ -8,11 +8,11 @@ import mikeio -from ..types import GeometryType, PointType +from ..types import GeometryType, PointType, network_gtype from ..quantity import Quantity from ..utils import _get_name from ._timeseries import _normalize_time_to_ns, _validate_data_var_name -from ._coords import XYZCoords, NodeCoords, ReachCoords +from ._coords import XYZCoords, NetworkCoords @dataclass @@ -145,7 +145,7 @@ def _convert_to_dataset( def _include_coords( ds: xr.Dataset, *, - coords: XYZCoords | NodeCoords | ReachCoords | None = None, + coords: XYZCoords | NetworkCoords | None = None, ) -> xr.Dataset: ds = ds.copy() if coords is not None: @@ -168,12 +168,7 @@ def _include_attributes( ) -> xr.Dataset: ds = ds.copy() - if "node" in ds.coords or ("reach" in ds.coords and "distance" in ds.coords): - ds.attrs["gtype"] = str(GeometryType.NODE) - elif "reach" in ds.coords: - ds.attrs["gtype"] = str(GeometryType.REACH) - else: - ds.attrs["gtype"] = str(GeometryType.POINT) + ds.attrs["gtype"] = str(network_gtype(ds) or GeometryType.POINT) ds[name].attrs["long_name"] = quantity.name ds[name].attrs["units"] = quantity.unit @@ -247,7 +242,7 @@ def _parse_point_input( quantity: Quantity | None, aux_items: Sequence[int | str] | None, *, - coords: XYZCoords | NodeCoords | ReachCoords, + coords: XYZCoords | NetworkCoords, ) -> xr.Dataset: """Convert accepted input data to an xr.Dataset.""" @@ -279,39 +274,3 @@ def _parse_xyz_point_input( coords = XYZCoords(x, y, z) ds = _parse_point_input(data, name, item, quantity, aux_items, coords=coords) return ds - - -def _parse_network_node_input( - data: PointType, - name: str | None, - item: str | int | None, - quantity: Quantity | None, - node: int | str | None, - aux_items: Sequence[int | str] | None, -) -> xr.Dataset: - if node is None: - raise ValueError("'node' argument cannot be empty.") - coords = NodeCoords(node=node) - ds = _parse_point_input(data, name, item, quantity, aux_items, coords=coords) - return ds - - -def _parse_network_breakpoint_input( - data: PointType, - name: str | None, - item: str | int | None, - quantity: Quantity | None, - aux_items: Sequence[int | str] | None, - *, - reach: str, - distance: float | None = None, -) -> xr.Dataset: - """Parse input for a breakpoint (or reach-level) observation. - - When ``distance`` is ``None`` the observation is reach-level — no - ``distance`` coordinate is stored and the result can be matched to any - breakpoint on the reach. - """ - coords = ReachCoords(reach=reach, distance=distance) - ds = _parse_point_input(data, name, item, quantity, aux_items, coords=coords) - return ds diff --git a/src/modelskill/timeseries/_timeseries.py b/src/modelskill/timeseries/_timeseries.py index bba5d78f7..1ad7ec86b 100644 --- a/src/modelskill/timeseries/_timeseries.py +++ b/src/modelskill/timeseries/_timeseries.py @@ -8,13 +8,15 @@ import pandas as pd import xarray as xr -from ..types import GeometryType +from ..types import GeometryType, network_gtype from ..quantity import Quantity +from ._coords import NETWORK_LOCATION_COORDS, _coordinate_values from ._plotter import TimeSeriesPlotter, MatplotlibTimeSeriesPlotter from .. import __version__ T = TypeVar("T", bound="TimeSeries") + DEFAULT_COLORS = [ "#b30000", "#7c1158", @@ -31,7 +33,7 @@ def _validate_data_var_name(name: str) -> str: if not isinstance(name, str): raise TypeError("name must be a string") - RESERVED_NAMES = ["x", "y", "z", "time"] + RESERVED_NAMES = ["x", "y", "z", "time", *NETWORK_LOCATION_COORDS] if name in RESERVED_NAMES: raise ValueError( f"name '{name}' is reserved and cannot be used! Please choose another name." @@ -101,19 +103,12 @@ def _validate_dataset(ds: xr.Dataset) -> xr.Dataset: # Validate coordinates: x,y spatial, node-based, or reach-based (with or without chainage) has_spatial_coords = "x" in ds.coords and "y" in ds.coords - has_node_coord = "node" in ds.coords - has_breakpoint_coords = "reach" in ds.coords and "distance" in ds.coords - has_reach_coord = "reach" in ds.coords and "distance" not in ds.coords - - if ( - not has_spatial_coords - and not has_node_coord - and not has_breakpoint_coords - and not has_reach_coord - ): + has_network_coords = network_gtype(ds) is not None + + if not has_spatial_coords and not has_network_coords: raise ValueError( "data must have either x,y coordinates, a node coordinate, " - "reach+distance coordinates, or a reach coordinate" + "reach+position coordinates, or a reach coordinate" ) if has_spatial_coords and "z" not in ds.coords: @@ -263,16 +258,8 @@ def y(self) -> Any: def y(self, value: Any) -> None: self.data["y"] = value - @property - def node(self) -> Any: - """node-coordinate""" - return self._coordinate_values("node") - - def _coordinate_values(self, coord: str) -> None | float | np.ndarray: - if coord not in self.data.coords: - return None # Node-based data doesn't have y coordinate - vals = self.data[coord].values - return np.atleast_1d(vals)[0] if vals.ndim == 0 else vals + def _coordinate_values(self, coord: str) -> Any: + return _coordinate_values(self.data, coord) @property def _is_modelresult(self) -> bool: @@ -292,16 +279,18 @@ def _values_as_series(self) -> pd.Series: def _aux_vars(self): return list(self.data.filter_by_attrs(kind="aux").data_vars) + def _location_repr(self) -> str | None: + """The location line for ``__repr__``, or None when there is nothing to say.""" + if self.gtype == str(GeometryType.POINT): + if self.x is not None and self.y is not None: + return f"Location: {self.x}, {self.y}" + return None + def __repr__(self) -> str: res = [] res.append(f"<{self.__class__.__name__}>: {self.name}") - if self.gtype == str(GeometryType.POINT): - # Show location based on available coordinates - if "node" in self.data.coords: - node_id = self.data.coords["node"].item() - res.append(f"Node: {node_id}") - elif self.x is not None and self.y is not None: - res.append(f"Location: {self.x}, {self.y}") + if (location := self._location_repr()) is not None: + res.append(location) res.append(f"Time: {self.time[0]} - {self.time[-1]}") res.append(f"Quantity: {self.quantity}") if len(self._aux_vars) > 0: @@ -366,10 +355,12 @@ def to_dataframe(self) -> pd.DataFrame: return df[cols] elif self.gtype == str(GeometryType.VERTICAL): return self.data.drop_vars(["x", "y"]).to_dataframe() - elif self.gtype == str(GeometryType.NODE): - return self.data.drop_vars(["node"]).to_dataframe() - elif self.gtype == str(GeometryType.REACH): - return self.data.drop_vars(["reach"]).to_dataframe() + elif self.gtype in (str(GeometryType.NODE), str(GeometryType.REACH)): + # A breakpoint carries reach and position rather than node, so drop + # whichever of them this one has. + return self.data.drop_vars( + NETWORK_LOCATION_COORDS, errors="ignore" + ).to_dataframe() else: raise NotImplementedError(f"Unknown gtype: {self.gtype}") diff --git a/src/modelskill/types.py b/src/modelskill/types.py index cf9e2a390..51a51c117 100644 --- a/src/modelskill/types.py +++ b/src/modelskill/types.py @@ -17,6 +17,7 @@ class GeometryType(Enum): VERTICAL = "vertical" NODE = "node" REACH = "reach" + NETWORK = "network" def __str__(self) -> str: return self.name.lower() @@ -44,6 +45,8 @@ def from_string(s: str) -> "GeometryType": >>> GeometryType.from_string("node") + >>> GeometryType.from_string("network") + """ try: @@ -57,6 +60,44 @@ def from_string(s: str) -> "GeometryType": ) from e +def network_gtype(ds: xr.Dataset) -> GeometryType | None: + """The geometry of a network timeseries, read from its coordinates. + + Only network coordinates are read. Data located some other way, by x and + y for instance, records no network location and gives None rather than + the geometry it does have. + + Parameters + ---------- + ds : xr.Dataset + Dataset to inspect. + + Returns + ------- + GeometryType or None + NODE for a node or a break point, REACH for a whole reach, and None + for data that carries no network location. + + Examples + -------- + >>> import xarray as xr + >>> from modelskill.types import network_gtype + >>> network_gtype(xr.Dataset(coords={"node": "123"})) + + >>> network_gtype(xr.Dataset(coords={"reach": "r1", "position": 24.5})) + + >>> network_gtype(xr.Dataset(coords={"reach": "r1"})) + + >>> network_gtype(xr.Dataset(coords={"x": 0.0, "y": 0.0})) is None + True + """ + if "node" in ds.coords or {"reach", "position"} <= set(ds.coords): + return GeometryType.NODE + if "reach" in ds.coords: + return GeometryType.REACH + return None + + DataInputType = Union[ str, Path, diff --git a/src/modelskill/utils.py b/src/modelskill/utils.py index 43435dd93..8faed5531 100644 --- a/src/modelskill/utils.py +++ b/src/modelskill/utils.py @@ -6,7 +6,7 @@ import xarray as xr from collections.abc import Hashable, Iterable -_RESERVED_NAMES = ["Observation", "time", "x", "y", "z"] +_RESERVED_NAMES = ["Observation", "time", "x", "y", "z", "node", "reach", "position"] POS_COORDINATE_NAME_MAPPING = { "lon": "x", diff --git a/tests/network_helpers.py b/tests/network_helpers.py new file mode 100644 index 000000000..8da23725c --- /dev/null +++ b/tests/network_helpers.py @@ -0,0 +1,85 @@ +"""Helpers shared by the tests that score a network model result. + +A Network comes from a result file and from nothing else, so these open the test +data rather than building a topology by hand. Importing this module needs +mikeio1d, which is an optional dependency (ADR-010), so guard the import with +``pytest.importorskip("mikeio1d.network")`` first. +""" + +from __future__ import annotations + +from pathlib import Path + +import numpy as np +import pandas as pd +from mikeio1d import Res1D +from mikeio1d.network import Network + +_TESTDATA = Path(__file__).parent / "testdata" + +RES1D = str(_TESTDATA / "network.res1d") +"""A MIKE urban result: WaterLevel on every node, Discharge on one gridpoint per reach.""" + +EPANET = str(_TESTDATA / "epanet.res") +"""A link-node result, read with the .resx beside it. + +Its nodes each carry several quantities, which no node of RES1D does. +""" + +NODE_IDS = ["1", "2", "3"] +"""Three nodes of RES1D, each carrying a WaterLevel series of its own.""" + +EPANET_NODES = ["11", "12"] +"""Two nodes of EPANET. Not '10', which is also the id of a reach there.""" + +REACH = "100l1" +REACH_ITEM = "Discharge" +POSITION = 23.8413574216414 +"""Where REACH keeps its one REACH_ITEM gridpoint; its neighbours carry WaterLevel.""" + +BREAKPOINT = (REACH, POSITION) + + +def open_network(path: str = RES1D) -> Network: + """The test data as a Network.""" + return Network.open(path) + + +def modified_network( + tmp_path, *, offset: float = 0.0, blank: bool = False, path: str = RES1D +) -> Network: + """A copy of `path` with every value shifted by `offset`, or blanked to NaN. + + Two model results have to differ before a crossed model column can show, and + a break point that names a quantity while holding nothing for it is a state + no fixture file is in. Both are written out rather than assembled in memory, + since a Network comes from a file and from nothing else. + """ + out = Path(tmp_path) / "modified.res1d" + res = Res1D(path) + values = res.read(column_mode="all") + res.modify(values * np.nan if blank else values + offset) + res.save(str(out)) + return Network.open(str(out)) + + +def node_series(network, quantity="WaterLevel", nodes=None) -> pd.DataFrame: + """Each node's own series for `quantity`, keyed by node id. + + Restricted to `nodes` when given, since a result file holds more of them than + a test wants to reason about, and the order is the one asked for. Otherwise + every node carrying the quantity, in the order the network lists them. + """ + if nodes is None: + # Break points carry the quantity too, and are named by (reach, position). + nodes = [a for a in network.addresses(quantity=quantity) if isinstance(a, str)] + df = network.read([(node_id, quantity) for node_id in nodes]) + df.columns = list(nodes) + return df + + +def breakpoint_series(network, address=BREAKPOINT, quantity=REACH_ITEM) -> pd.DataFrame: + """The series of one break point, in a column named for its quantity.""" + df = network.read([(address, quantity)]) + df.columns = [quantity] + return df diff --git a/tests/notebooks/test_notebooks.py b/tests/notebooks/test_notebooks.py index 8b6fd62dc..3a7ba953f 100644 --- a/tests/notebooks/test_notebooks.py +++ b/tests/notebooks/test_notebooks.py @@ -1,5 +1,6 @@ import os import subprocess +import sys import nbformat from nbconvert.preprocessors import ExecutePreprocessor @@ -7,8 +8,11 @@ _TEST_DIR = os.path.dirname(os.path.abspath(__file__)) PARENT_DIR = os.path.join(_TEST_DIR, "../..") -SKIP_LIST = ["Download", "Metocean_track_comparison_global", "Metrics_widget", "Collection_systems_network"] -# We skip Collection_systems_network.ipynb since it uses Network.from_mike() which uses pythonnet and, currently, it does not support python 3.14 +SKIP_LIST = ["Download", "Metocean_track_comparison_global", "Metrics_widget"] +if sys.version_info >= (3, 15): + # Collection_systems_network.ipynb reads a res1d through mikeio1d, whose + # pythonnet has no 3.15 wheel. + SKIP_LIST.append("Collection_systems_network") def _process_notebook(notebook_filename, notebook_path="notebooks"): diff --git a/tests/test_comparer.py b/tests/test_comparer.py index 97d4b1a93..e161dead3 100644 --- a/tests/test_comparer.py +++ b/tests/test_comparer.py @@ -312,6 +312,8 @@ def test_rename_fails_reserved_names(pt_df): cmp.rename({"m1": "z"}) with pytest.raises(ValueError, match="reserved names!"): cmp.rename({"m1": "Observation"}) + with pytest.raises(ValueError, match="reserved names!"): + cmp.rename({"m1": "node"}) def test_matched_df_illegal_items(pt_df): diff --git a/tests/test_comparercollection.py b/tests/test_comparercollection.py index bdf4aa807..1f4a06ca6 100644 --- a/tests/test_comparercollection.py +++ b/tests/test_comparercollection.py @@ -454,26 +454,30 @@ def test_save_and_load_preserves_raw_model_data(cc, tmp_path): @pytest.fixture def node_comparer() -> modelskill.comparison.Comparer: """A comparer built by matching a NodeObservation against a NetworkModelResult (node gtype).""" - pytest.importorskip("networkx") - from modelskill.model.network import NetworkModelResult - from modelskill.network import Network, BasicNode, BasicReach - from modelskill.obs import NodeObservation - - time = pd.date_range("2019-01-01", periods=6, freq="D") - node_a_data = pd.DataFrame( - {"WaterLevel": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]}, index=time - ) - node_b_data = pd.DataFrame( - {"WaterLevel": [1.1, 2.2, 3.3, 4.4, 5.5, 6.6]}, index=time - ) - reach = BasicReach( - "r1", BasicNode("123", node_a_data), BasicNode("456", node_b_data), length=100.0 - ) - network = Network([reach]) + pytest.importorskip("mikeio1d.network") + from tests.network_helpers import NODE_IDS, node_series, open_network - nmr = NetworkModelResult(network, name="Network_Model") - node_id = network.find(node="123") - obs = NodeObservation(node_a_data, at=node_id, name="Node_123_Obs") + network = open_network() + node_id = NODE_IDS[0] + values = node_series(network, nodes=NODE_IDS)[[node_id]] + + nmr = ms.NetworkModelResult(network, item="WaterLevel", name="Network_Model") + obs = ms.NodeObservation(values, at=node_id, item=node_id, name="Node_Obs") + + return ms.match(obs, nmr) + + +@pytest.fixture +def reach_comparer() -> modelskill.comparison.Comparer: + """A comparer built by matching a ReachObservation (reach gtype).""" + pytest.importorskip("mikeio1d.network") + from tests.network_helpers import REACH, REACH_ITEM, breakpoint_series, open_network + + network = open_network() + values = breakpoint_series(network) + + nmr = ms.NetworkModelResult(network, item=REACH_ITEM, name="Network_Model") + obs = ms.ReachObservation(values, reach=REACH, name="Reach_Obs") return ms.match(obs, nmr) @@ -490,6 +494,61 @@ def test_save_and_load_round_trips_node_gtype_raw_data(node_comparer, tmp_path): assert len(cc2[0].raw_mod_data["Network_Model"]) == len( node_comparer.raw_mod_data["Network_Model"] ) + # The node was addressed by name, and the name is what comes back: reloading + # must not depend on the integer the network happened to hand out. + assert cc2[0].node == node_comparer.node + assert cc2[0].raw_mod_data["Network_Model"].node == node_comparer.node + + +def test_save_and_load_round_trips_reach_gtype_raw_data(reach_comparer, tmp_path): + """Reach-gtype comparers must survive a save()/load() round trip too.""" + cc = ms.ComparerCollection([reach_comparer]) + fn = tmp_path / "test_cc_reach.msk" + cc.save(fn) + + cc2 = ms.load(fn) + + assert cc2[0].gtype == "reach" + assert cc2[0].reach == reach_comparer.reach + assert cc2[0].position is None + assert ( + cc2[0].raw_mod_data["Network_Model"].at + == reach_comparer.raw_mod_data["Network_Model"].at + ) + assert len(cc2[0].raw_mod_data["Network_Model"]) == len( + reach_comparer.raw_mod_data["Network_Model"] + ) + + +def test_to_dataframe_on_a_node_comparer(node_comparer): + df = node_comparer.to_dataframe() + + assert list(df.columns) == ["Observation", "Network_Model"] + assert df.index.name == "time" + + +def test_to_dataframe_on_a_reach_comparer(reach_comparer): + """The location coordinates are dropped, as they are for every other gtype.""" + df = reach_comparer.to_dataframe() + + assert list(df.columns) == ["Observation", "Network_Model"] + assert df.index.name == "time" + + +def test_skill_on_a_node_comparer(node_comparer): + sk = node_comparer.skill() + + assert sk.to_dataframe().shape[0] == 1 + assert "Node_Obs" in sk.index + + +def test_plot_a_node_comparer(node_comparer): + assert node_comparer.plot.timeseries() is not None + assert node_comparer.plot.scatter() is not None + + +def test_plot_a_reach_comparer(reach_comparer): + assert reach_comparer.plot.timeseries() is not None # ======================== plotting ======================== diff --git a/tests/test_match.py b/tests/test_match.py index 3324f4788..0d0032269 100644 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -7,10 +7,6 @@ import modelskill as ms from modelskill.comparison._comparison import ItemSelection from modelskill.model.dfsu import DfsuModelResult -try: - from modelskill.network import _make_basic_network -except ImportError: - pass @pytest.fixture @@ -339,35 +335,46 @@ def test_only_1_model_depth_overlap(self, simple_vo, simple_vm): # Network-related fixtures @pytest.fixture def network(): - """Network fixture with 3 nodes""" - pytest.importorskip("networkx") - time = pd.date_range("2017-10-27", periods=20, freq="h") - np.random.seed(42) - data = np.random.normal(1.5, 0.3, (20, 3)) - return _make_basic_network(["100", "200", "300"], time, data) + """A MIKE urban result, whose nodes carry WaterLevel.""" + pytest.importorskip("mikeio1d.network") + from tests.network_helpers import open_network + + return open_network() + + +@pytest.fixture +def network2(tmp_path): + """The same result shifted by 0.1, for the multi-model tests. + + Two model results have to differ before a crossed model column can show. + """ + pytest.importorskip("mikeio1d.network") + from tests.network_helpers import modified_network + + return modified_network(tmp_path, offset=0.1) @pytest.fixture -def network2(): - """Second network fixture with offset data for multi-model tests""" - pytest.importorskip("networkx") - time = pd.date_range("2017-10-27", periods=20, freq="h") - np.random.seed(42) - data = np.random.normal(1.5, 0.3, (20, 3)) + 0.1 - return _make_basic_network(["100", "200", "300"], time, data) +def model_time(network): + """The result file's own time axis, which an observation has to overlap.""" + from tests.network_helpers import node_series + + return node_series(network, nodes=["100"]).index @pytest.fixture def network_mr(network): """NetworkModelResult fixture""" - return ms.NetworkModelResult(network, name="Network_Model") + return ms.NetworkModelResult(network, item="WaterLevel", name="Network_Model") @pytest.fixture -def node_obs1(network): +def node_obs1(model_time): """NodeObservation for node '100'""" - node_id = network.find(node="100") - time = pd.date_range("2017-10-27", periods=18, freq="h") + node_id = "100" + # On the model's own time axis, trimmed: an observation is matched only + # where it overlaps the model. + time = model_time[:18] # Add some noise to make it different from model np.random.seed(123) data = np.random.normal(1.4, 0.2, len(time)) @@ -376,10 +383,10 @@ def node_obs1(network): @pytest.fixture -def node_obs2(network): - """NodeObservation for node '200'""" - node_id = network.find(node="200") - time = pd.date_range("2017-10-27", periods=15, freq="h") +def node_obs2(model_time): + """NodeObservation for node '101'""" + node_id = "101" + time = model_time[:15] np.random.seed(456) data = np.random.normal(1.6, 0.25, len(time)) df = pd.DataFrame({"WaterLevel": data}, index=time) @@ -387,31 +394,31 @@ def node_obs2(network): @pytest.fixture -def node_obs_invalid(network): +def node_obs_invalid(model_time): """NodeObservation for a node that doesn't exist in the network""" - time = pd.date_range("2017-10-27", periods=10, freq="h") + time = model_time[:10] data = np.random.normal(1.5, 0.2, len(time)) df = pd.DataFrame({"WaterLevel": data}, index=time) - return ms.NodeObservation(df, at=999, name="Node_999_Obs") + return ms.NodeObservation(df, at="999", name="Node_999_Obs") @pytest.fixture def network_mr1(network): """First NetworkModelResult fixture""" - return ms.NetworkModelResult(network, name="Network_1") + return ms.NetworkModelResult(network, item="WaterLevel", name="Network_1") @pytest.fixture def network_mr2(network2): """Second NetworkModelResult fixture with offset data""" - return ms.NetworkModelResult(network2, name="Network_2") + return ms.NetworkModelResult(network2, item="WaterLevel", name="Network_2") @pytest.fixture -def node_obs_gaps(network): +def node_obs_gaps(model_time): """NodeObservation with time gaps""" - node_id = network.find(node="100") - time = pd.date_range("2017-10-27", periods=10, freq="2h") # Different frequency + node_id = "100" + time = model_time[::2][:10] # Every other step of the model's data = np.random.normal(1.5, 0.2, len(time)) df = pd.DataFrame({"WaterLevel": data}, index=time) return ms.NodeObservation(df, at=node_id, name="Node_100_Gaps") @@ -420,7 +427,7 @@ def node_obs_gaps(network): @pytest.fixture def network_mr_gaps(network): """NetworkModelResult for gap testing""" - return ms.NetworkModelResult(network, name="Network_Gaps") + return ms.NetworkModelResult(network, item="WaterLevel", name="Network_Gaps") @pytest.fixture @@ -1007,6 +1014,31 @@ def test_match_node_obs_with_network_model(node_obs1, network_mr): assert cmp.mod_names == ["Network_Model"] +def test_match_reach_obs_with_network_model(): + """A reach observation matches any breakpoint along the reach.""" + pytest.importorskip("mikeio1d.network") + from tests.network_helpers import REACH, REACH_ITEM, breakpoint_series, open_network + + network = open_network() + network_mr = ms.NetworkModelResult(network, item=REACH_ITEM, name="Network_Model") + + np.random.seed(123) + obs_time = breakpoint_series(network).index[:18] + df = pd.DataFrame( + {REACH_ITEM: np.random.normal(1.4, 0.2, len(obs_time))}, index=obs_time + ) + obs = ms.ReachObservation(df, reach=REACH, name="Reach_Obs") + + cmp = ms.match(obs, network_mr) + + assert cmp.n_models == 1 + assert cmp.n_points == 18 + assert cmp.name == "Reach_Obs" + assert cmp.gtype == "reach" + assert cmp.reach == REACH + assert cmp.mod_names == ["Network_Model"] + + def test_match_multiple_node_obs_with_network(node_obs1, node_obs2, network_mr): cc = ms.match([node_obs1, node_obs2], network_mr) assert cc.n_models == 1 @@ -1027,7 +1059,7 @@ def test_match_node_obs_with_multiple_network_models( def test_match_network_invalid_node_error(node_obs_invalid, network_mr): - with pytest.raises(ValueError, match="Node 999 not found"): + with pytest.raises(ValueError, match="not found"): ms.match(node_obs_invalid, network_mr) @@ -1064,7 +1096,8 @@ def test_network_match_multi_obs_multi_model_comprehensive( def test_network_match_error_non_node_observation(network_mr, point_obs_error): """Test that non-NodeObservation raises appropriate error""" with pytest.raises( - TypeError, match="NetworkModelResult supports NodeObservation and ReachObservation" + TypeError, + match="NetworkModelResult supports NodeObservation and ReachObservation", ): ms.match(point_obs_error, network_mr) diff --git a/tests/test_network.py b/tests/test_network.py index a41fc997b..c739c4a93 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -1,132 +1,66 @@ """Test network models and observations""" # ruff: noqa: E402 -import sys -from pathlib import Path +from dataclasses import dataclass + import pytest -pytest.importorskip("networkx") +pytest.importorskip("mikeio1d.network") import pandas as pd import xarray as xr import numpy as np +import mikeio1d import modelskill as ms -from modelskill.model.network import ( +from mikeio1d.network import Network +from modelskill import ( NetworkModelResult, - NodeModelResult, -) -from modelskill.model.adapters._inp import read_pipe_lengths, read_sections -from modelskill.model.adapters._res1d import ( - Res1DNode, - Res1DReach, - _simplify_colnames, -) -from modelskill.network import ( - Network, - BasicNode, - BasicReach, - NetworkReach, - ReachBreakPoint, - _EPANET_EXTENSIONS, - _MIKE_EXTENSIONS, - _UNSUPPORTED_EXTENSIONS, + NodeObservation, + Quantity, + ReachObservation, ) -from modelskill.obs import NodeObservation -from modelskill.quantity import Quantity - - -def _make_network(node_ids, time, data, quantity="WaterLevel"): - nodes = [ - BasicNode(nid, pd.DataFrame({quantity: data[:, i]}, index=time)) - for i, nid in enumerate(node_ids) - ] - reaches = [ - BasicReach(f"r{i}", nodes[i], nodes[i + 1], length=100.0) - for i in range(len(nodes) - 1) - ] - return Network(reaches) - -@pytest.fixture -def sample_network_data(): - """Sample network data as xr.Dataset""" - time = pd.date_range("2010-01-01", periods=10, freq="h") - nodes = [123, 456, 789] - - # Create sample data - np.random.seed(42) # For reproducible tests - data = np.random.randn(len(time), len(nodes)) - - ds = xr.Dataset( - { - "WaterLevel": (["time", "node"], data), - }, - coords={ - "time": time, - "node": nodes, - }, - ) - ds["WaterLevel"].attrs["units"] = "m" - ds["WaterLevel"].attrs["long_name"] = "Water Level" - - return ds +from modelskill.model.network import NodeModelResult + +from tests.network_helpers import ( + BREAKPOINT, + EPANET, + NODE_IDS, + POSITION, + breakpoint_series, + REACH, + REACH_ITEM, + modified_network, + node_series, + open_network, +) @pytest.fixture def sample_network(): - """Sample Network with 3 nodes (WaterLevel quantity)""" - time = pd.date_range("2010-01-01", periods=10, freq="h") - np.random.seed(42) - data = np.random.randn(10, 3) - return _make_network(["123", "456", "789"], time, data) + """A MIKE urban result: WaterLevel on its nodes, Discharge on gridpoints. + + A MIKE reach keeps Discharge on one interior gridpoint and WaterLevel on the + ones either side, so the break point at BREAKPOINT holds a series neither of + the reach's nodes has, and it is the only break point of REACH that does. + """ + return open_network() @pytest.fixture def sample_network_multivars(): - """Sample Network with 2 nodes and 2 quantities (WaterLevel + Discharge)""" - time = pd.date_range("2010-01-01", periods=10, freq="h") - np.random.seed(42) - raw = np.random.randn(10, 2) - nodes = [ - BasicNode( - nid, - pd.DataFrame( - {"WaterLevel": raw[:, i], "Discharge": raw[:, i] * 10}, - index=time, - ), - ) - for i, nid in enumerate(["123", "456"]) - ] - reaches = [BasicReach("r1", nodes[0], nodes[1], length=100.0)] - return Network(reaches) + """An EPANET result, whose every node carries several quantities at once.""" + return open_network(EPANET) @pytest.fixture -def dataset_without_node(): - time = pd.date_range("2010-01-01", periods=10, freq="h") - - # Create sample data - np.random.seed(42) # For reproducible tests - data = np.random.randn(len(time)) - - ds = xr.Dataset( - { - "WaterLevel": (["time"], data), - }, - coords={ - "time": time, - }, - ) - ds["WaterLevel"].attrs["units"] = "m" - ds["WaterLevel"].attrs["long_name"] = "Water Level" - - return ds +def sample_node_data(sample_network): + """Sample node observation data, on the result file's own time axis. - -@pytest.fixture -def sample_node_data(): - """Sample node observation data""" - time = pd.date_range("2010-01-01", periods=10, freq="h") + An observation is only matched where it overlaps the model, so it has to be + stamped with the times the file has rather than a round decade of its own. + """ + time = node_series(sample_network, nodes=NODE_IDS).index # Create sample data with some variation np.random.seed(42) @@ -148,26 +82,51 @@ class TestNetworkModelResult: def test_init_with_network(self, sample_network): """Test initialization with a Network object""" - nmr = NetworkModelResult(sample_network) + nmr = NetworkModelResult(sample_network, item="WaterLevel") + + assert nmr.period == sample_network.period + + def test_quantity_name_survives_to_the_model_result(self, sample_network): + """The network knows its quantity by name even without a unit.""" + nmr = NetworkModelResult(sample_network, item="WaterLevel") - assert len(nmr.time) == 10 - assert isinstance(nmr.time, pd.DatetimeIndex) - assert len(nmr.nodes) == 3 + assert nmr.quantity.name == "WaterLevel" + assert nmr.quantity != Quantity.undefined() + + def test_the_unit_comes_from_the_result_file(self, sample_network): + nmr = NetworkModelResult(sample_network, item="WaterLevel") + + assert nmr.quantity == Quantity(name="WaterLevel", unit="m") + + def test_quantity_carries_into_extracted_node( + self, sample_network, sample_node_data + ): + nmr = NetworkModelResult(sample_network, item="WaterLevel") + extracted = nmr.extract(NodeObservation(sample_node_data, at="1")) + + assert extracted.quantity.name == "WaterLevel" + + def test_explicit_quantity_wins(self, sample_network): + given = Quantity(name="Water Level", unit="meter") + nmr = NetworkModelResult(sample_network, item="WaterLevel", quantity=given) + + assert nmr.quantity == given def test_init_with_name(self, sample_network): """Test initialization with explicit name""" - nmr = NetworkModelResult(sample_network, name="Test_Network") + nmr = NetworkModelResult(sample_network, item="WaterLevel", name="Test_Network") assert nmr.name == "Test_Network" - def test_init_with_item_selection(self, sample_network_multivars): + def test_init_with_item_selection(self, sample_network_multivars, sample_node_data): """Test initialization with specific item selection""" nmr = NetworkModelResult( - sample_network_multivars, item="WaterLevel", name="Network_WL" + sample_network_multivars, item="Head", name="Network_WL" ) + extracted = nmr.extract(NodeObservation(sample_node_data, at="11")) assert nmr.name == "Network_WL" - assert "WaterLevel" in nmr.data.data_vars - assert "Discharge" not in nmr.data.data_vars + assert nmr.quantity.name == "Head" + assert extracted.quantity.name == "Head" def test_init_fails_with_unsupported_type(self): """Test that passing a non-Network object raises an error""" @@ -176,7 +135,7 @@ def test_init_fails_with_unsupported_type(self): def test_repr(self, sample_network): """Test string representation""" - nmr = NetworkModelResult(sample_network, name="Test_Network") + nmr = NetworkModelResult(sample_network, item="WaterLevel", name="Test_Network") repr_str = repr(nmr) assert "NetworkModelResult" in repr_str @@ -184,27 +143,26 @@ def test_repr(self, sample_network): def test_extract_valid_node(self, sample_network, sample_node_data): """Test extraction of a valid node""" - nmr = NetworkModelResult(sample_network) - node_id = sample_network.find(node="123") - obs = NodeObservation(sample_node_data, at=node_id, name="Node_123") + nmr = NetworkModelResult(sample_network, item="WaterLevel") + node_id = "1" + obs = NodeObservation(sample_node_data, at=node_id, name="Node_1") extracted = nmr.extract(obs) - assert isinstance(extracted, NodeModelResult) assert extracted.node == node_id - assert len(extracted.time) == 10 + assert len(extracted.time) == len(sample_node_data) def test_extract_invalid_node(self, sample_network, sample_node_data): """Test extraction of a node not present in the network""" - nmr = NetworkModelResult(sample_network) - obs = NodeObservation(sample_node_data, at=999, name="Node_999") + nmr = NetworkModelResult(sample_network, item="WaterLevel") + obs = NodeObservation(sample_node_data, at="999", name="Node_999") - with pytest.raises(ValueError, match="Node 999 not found"): + with pytest.raises(ValueError, match="not found"): nmr.extract(obs) def test_extract_wrong_observation_type(self, sample_network): """Test extraction with wrong observation type""" - nmr = NetworkModelResult(sample_network) + nmr = NetworkModelResult(sample_network, item="WaterLevel") df = pd.DataFrame( {"WL": [1, 2, 3]}, index=pd.date_range("2010-01-01", periods=3, freq="h") @@ -218,6 +176,45 @@ def test_extract_wrong_observation_type(self, sample_network): nmr.extract(obs) +class TestNodeModelResult: + """A model result at one network location, as extract() hands it back.""" + + def test_a_dataframe_is_refused(self, sample_node_data): + with pytest.raises(TypeError, match="takes an xarray.Dataset"): + NodeModelResult(sample_node_data) + + def test_data_located_by_x_and_y_is_refused(self, sample_node_data): + ds = xr.Dataset( + {"WaterLevel": ("time", sample_node_data["WaterLevel"].to_numpy())}, + coords={"time": sample_node_data.index, "x": 0.0, "y": 0.0}, + ) + + with pytest.raises(ValueError, match="needs data carrying a 'node' coordinate"): + NodeModelResult(ds) + + def test_a_dataset_carrying_a_node_is_accepted( + self, sample_network, sample_node_data + ): + extracted = NetworkModelResult( + sample_network, item="WaterLevel", name="Network_Model" + ).extract(NodeObservation(sample_node_data, at="1")) + + rebuilt = NodeModelResult(extracted.data) + + assert rebuilt.node == "1" + assert rebuilt.data["Network_Model"].attrs["kind"] == "model" + + def test_reading_an_observations_data_leaves_the_observation_alone( + self, sample_node_data + ): + """A result marks its own copy, so the observation stays an observation.""" + obs = NodeObservation(sample_node_data, at="123", item="WaterLevel") + + NodeModelResult(obs.data) + + assert obs.data["WaterLevel"].attrs["kind"] == "observation" + + class TestNodeObservation: """Test NodeObservation class""" @@ -236,26 +233,31 @@ def test_init_with_df(self, sample_node_data): """Test initialization with pandas DataFrame""" obs = NodeObservation( - sample_node_data, at=123, name="Sensor_1", item="WaterLevel" + sample_node_data, at="123", name="Sensor_1", item="WaterLevel" ) - assert obs.at == 123 + assert obs.at == "123" assert obs.name == "Sensor_1" - assert len(obs.time) == 10 + assert len(obs.time) == len(sample_node_data) assert isinstance(obs.time, pd.DatetimeIndex) + @pytest.mark.parametrize("name", ["node", "reach", "position"]) + def test_a_network_coordinate_name_is_reserved(self, sample_node_data, name): + with pytest.raises(ValueError, match="reserved"): + NodeObservation(sample_node_data, at="123", name=name, item="WaterLevel") + def test_init_with_series(self, sample_series): """Test initialization with pandas Series""" - obs = NodeObservation(sample_series, at=456, name="Node_456") + obs = NodeObservation(sample_series, at="456", name="Node_456") - assert obs.at == 456 + assert obs.at == "456" assert obs.name == "Node_456" - assert len(obs.time) == 10 + assert len(obs.time) == len(sample_series) def test_node_attrs(self, sample_node_data): """Test attrs property""" attrs = {"source": "test", "version": "1.0"} - obs = NodeObservation(sample_node_data, at=123, attrs=attrs, weight=2.5) + obs = NodeObservation(sample_node_data, at="123", attrs=attrs, weight=2.5) assert obs.attrs["source"] == "test" assert obs.attrs["version"] == "1.0" @@ -266,7 +268,7 @@ def test_multiple_nodes_returns_list_of_observations(self, multi_data): """Test that from_multiple returns a list of NodeObservation objects""" obs_list = NodeObservation.from_multiple( data=multi_data, - nodes={123: "station_0", 456: "station_1", 789: "station_2"}, + nodes={"123": "station_0", "456": "station_1", "789": "station_2"}, ) assert len(obs_list) == 3 @@ -275,17 +277,17 @@ def test_multiple_nodes_returns_list_of_observations(self, multi_data): def test_node_ids_are_assigned_correctly(self, multi_data): obs_list = NodeObservation.from_multiple( data=multi_data, - nodes={123: "station_0", 456: "station_1", 789: "station_2"}, + nodes={"123": "station_0", "456": "station_1", "789": "station_2"}, ) - assert obs_list[0].node == 123 - assert obs_list[1].node == 456 - assert obs_list[2].node == 789 + assert obs_list[0].node == "123" + assert obs_list[1].node == "456" + assert obs_list[2].node == "789" def test_names_derived_from_column_names(self, multi_data): obs_list = NodeObservation.from_multiple( data=multi_data, - nodes={123: "station_0", 456: "station_1", 789: "station_2"}, + nodes={"123": "station_0", "456": "station_1", "789": "station_2"}, ) assert obs_list[0].name == "station_0" @@ -301,12 +303,12 @@ def test_from_xarray_dataset(self, sample_node_data): coords={"time": sample_node_data.index}, ) obs_list = NodeObservation.from_multiple( - data=ds, nodes={123: "station_0", 456: "station_1"} + data=ds, nodes={"123": "station_0", "456": "station_1"} ) assert len(obs_list) == 2 - assert obs_list[0].node == 123 - assert obs_list[1].node == 456 + assert obs_list[0].node == "123" + assert obs_list[1].node == "456" def test_nodes_must_be_dict(self, multi_data): with pytest.raises(TypeError, match="'nodes' must be a dict"): @@ -316,7 +318,7 @@ def test_attrs_propagated_to_all_observations(self, multi_data): attrs = {"source": "sensor_array", "version": 2} obs_list = NodeObservation.from_multiple( data=multi_data, - nodes={1: "station_0", 2: "station_1", 3: "station_2"}, + nodes={"1": "station_0", "2": "station_1", "3": "station_2"}, attrs=attrs, ) @@ -326,38 +328,38 @@ def test_attrs_propagated_to_all_observations(self, multi_data): def test_init_from_csv(self): obs = NodeObservation( - "tests/testdata/network_sensor_1.csv", at=1, item="water_level@sens1" + "tests/testdata/network_sensor_1.csv", at="1", item="water_level@sens1" ) - assert obs.at == 1 + assert obs.at == "1" assert len(obs.time) == 110 assert isinstance(obs.time, pd.DatetimeIndex) def test_from_multiple_csvs_via_dict(self): obs_list = NodeObservation.from_multiple( nodes={ - 1: "tests/testdata/network_sensor_1.csv", - 2: "tests/testdata/network_sensor_2.csv", - 3: "tests/testdata/network_sensor_3.csv", + "1": "tests/testdata/network_sensor_1.csv", + "2": "tests/testdata/network_sensor_2.csv", + "3": "tests/testdata/network_sensor_3.csv", } ) assert len(obs_list) == 3 assert all(isinstance(obs, NodeObservation) for obs in obs_list) - assert obs_list[0].node == 1 - assert obs_list[1].node == 2 - assert obs_list[2].node == 3 + assert obs_list[0].node == "1" + assert obs_list[1].node == "2" + assert obs_list[2].node == "3" for obs in obs_list: assert len(obs.time) > 0 def test_nodes_dict_maps_node_to_item(self, multi_data): obs_list = NodeObservation.from_multiple( - data=multi_data, nodes={123: "station_0", 456: "station_1"} + data=multi_data, nodes={"123": "station_0", "456": "station_1"} ) assert len(obs_list) == 2 - assert obs_list[0].node == 123 - assert obs_list[1].node == 456 + assert obs_list[0].node == "123" + assert obs_list[1].node == "456" assert obs_list[0].name == "station_0" assert obs_list[1].name == "station_1" @@ -367,522 +369,564 @@ def test_nodes_none_raises(self, multi_data): def test_single_node_dict(self, sample_node_data): obs_list = NodeObservation.from_multiple( - data=sample_node_data, nodes={123: "WaterLevel"} + data=sample_node_data, nodes={"123": "WaterLevel"} ) assert len(obs_list) == 1 assert isinstance(obs_list[0], NodeObservation) - assert obs_list[0].node == 123 + assert obs_list[0].node == "123" + def test_nodes_keys_accept_aliases(self, multi_data): + obs_list = NodeObservation.from_multiple( + data=multi_data, nodes={"node_A": "station_0", "node_B": "station_1"} + ) -class TestNodeModelResult: - """Test NodeModelResult class""" + assert [obs.at for obs in obs_list] == ["node_A", "node_B"] - @pytest.mark.parametrize("fixture_name", ["sample_node_data", "sample_series"]) - def test_init_(self, request, fixture_name): - """Test initialization with pandas DataFrame""" - data = request.getfixturevalue(fixture_name) - nmr = NodeModelResult(data, node=123, name="Node_123_Model") + def test_nodes_keys_accept_breakpoints(self, multi_data): + obs_list = NodeObservation.from_multiple( + data=multi_data, + nodes={("reach_1", 24.5): "station_0", ("reach_1", 50.0): "station_1"}, + ) - assert nmr.node == 123 - assert nmr.name == "Node_123_Model" - assert len(nmr.time) == 10 + assert [obs.at for obs in obs_list] == [("reach_1", 24.5), ("reach_1", 50.0)] -class TestNetworkIntegration: - """Test integration between network models and observations""" +class TestReachObservationFromMultiple: + @pytest.fixture + def multi_data(self, sample_node_data): + return pd.DataFrame( + { + "station_0": sample_node_data["WaterLevel"].values, + "station_1": sample_node_data["WaterLevel"].values + 0.1, + }, + index=sample_node_data.index, + ) - def test_network_to_node_extraction(self, sample_network, sample_node_data): - """Test complete workflow from network model to node extraction""" - nmr = NetworkModelResult(sample_network, name="Network_Model") - node_id = sample_network.find(node="123") - obs = NodeObservation(sample_node_data, at=node_id, name="Node_123_Obs") + def test_returns_list_of_reach_observations(self, multi_data): + obs_list = ReachObservation.from_multiple( + data=multi_data, reaches={"reach_1": "station_0", "reach_2": "station_1"} + ) - extracted = nmr.extract(obs) + assert len(obs_list) == 2 + assert all(isinstance(obs, ReachObservation) for obs in obs_list) + assert [obs.reach for obs in obs_list] == ["reach_1", "reach_2"] + assert [obs.name for obs in obs_list] == ["station_0", "station_1"] + + def test_separate_data_sources(self): + obs_list = ReachObservation.from_multiple( + reaches={ + "reach_1": "tests/testdata/network_sensor_1.csv", + "reach_2": "tests/testdata/network_sensor_2.csv", + } + ) - assert isinstance(extracted, NodeModelResult) - assert extracted.node == node_id - assert extracted.name == "Network_Model" - assert len(extracted.time) == len(obs.time) + assert [obs.reach for obs in obs_list] == ["reach_1", "reach_2"] + assert all(len(obs.time) > 0 for obs in obs_list) - def test_matching_workflow(self, sample_network, sample_node_data): - """Test matching workflow with network data""" - nmr = NetworkModelResult(sample_network, name="Network_Model") - node_id = sample_network.find(node="123") - obs = NodeObservation(sample_node_data, at=node_id, name="Node_123_Obs") + def test_attrs_propagated(self, multi_data): + obs_list = ReachObservation.from_multiple( + data=multi_data, + reaches={"reach_1": "station_0"}, + attrs={"source": "sensor_array"}, + ) - comparer = ms.match(obs, nmr) + assert obs_list[0].attrs["source"] == "sensor_array" - assert comparer is not None - assert "Network_Model" in comparer.mod_names - assert comparer.n_points > 0 + def test_reaches_none_raises(self, multi_data): + with pytest.raises(ValueError, match="'reaches' argument is required"): + ReachObservation.from_multiple(data=multi_data, reaches=None) - def test_matching_workflow_multiple_nodes(self, sample_network, sample_node_data): - """Test matching workflow with multiple node observations""" - nmr = NetworkModelResult(sample_network, name="Network_Model") + def test_reaches_must_be_dict(self, multi_data): + with pytest.raises(TypeError, match="'reaches' must be a dict"): + ReachObservation.from_multiple(data=multi_data, reaches="reach_1") - multi_data = pd.DataFrame( - { - "station_0": sample_node_data["WaterLevel"], - "station_1": sample_node_data["WaterLevel"] + 0.1, - "station_2": sample_node_data["WaterLevel"] + 0.2, - } + +class TestValuesReachTheComparer: + """The series a comparer holds is the one the network keeps at that location. + + The observation is built from the model's own data, so an exact match is the + expected outcome and any mix-up between locations, quantities or models shows + up as a non-zero score. + """ + + def test_extract_returns_the_nodes_own_series(self, sample_network): + values = node_series(sample_network, nodes=NODE_IDS) + nmr = NetworkModelResult( + sample_network, item="WaterLevel", name="Network_Model" ) + obs = NodeObservation(values, at="2", item="2") - node_0 = sample_network.find(node="123") - node_1 = sample_network.find(node="456") - node_2 = sample_network.find(node="789") + extracted = nmr.extract(obs) - # Create multiple NodeObservations using .from_multiple - obs_list = NodeObservation.from_multiple( - data=multi_data, - nodes={node_0: "station_0", node_1: "station_1", node_2: "station_2"}, + assert extracted.to_dataframe()["Network_Model"].to_numpy() == pytest.approx( + values["2"].to_numpy() ) - # Test that matching works - comparer_collection = ms.match(obs_list, nmr) + def test_a_matched_node_scores_zero_against_its_own_series(self, sample_network): + values = node_series(sample_network, nodes=NODE_IDS) + nmr = NetworkModelResult( + sample_network, item="WaterLevel", name="Network_Model" + ) + obs = NodeObservation(values, at="2", item="2", name="Node_2_Obs") - assert comparer_collection is not None - assert len(comparer_collection) == 3 + cmp = ms.match(obs, nmr) - for comparer in comparer_collection: - assert "Network_Model" in comparer.mod_names - assert comparer.n_points > 0 + assert cmp.n_points == len(values) + assert cmp.score()["Network_Model"] == pytest.approx(0.0) + def test_every_node_of_a_collection_keeps_its_own_series(self, sample_network): + """One observation per node, each read from that node's own column.""" + values = node_series(sample_network, nodes=NODE_IDS) + nmr = NetworkModelResult( + sample_network, item="WaterLevel", name="Network_Model" + ) + obs_list = NodeObservation.from_multiple( + data=values, nodes={nid: nid for nid in values.columns} + ) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_open_res1d(): - path_to_file = "./tests/testdata/network.res1d" - network = Network.from_mike(path_to_file) - assert network.graph.number_of_nodes() == 259 + cc = ms.match(obs_list, nmr) + assert len(cc) == 3 + for cmp in cc: + assert cmp.score()["Network_Model"] == pytest.approx(0.0) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_extract_reach_observation_happy_path(sample_node_data): - path_to_file = "./tests/testdata/network.res1d" - network = Network.from_mike(path_to_file) - nmr = NetworkModelResult(network, item="Discharge", name="network_model") - obs_data = sample_node_data.rename(columns={"WaterLevel": "Discharge"}) - obs = ms.ReachObservation(obs_data, reach="100l1", item="Discharge") + def test_two_networks_keep_their_series_apart(self, sample_network, tmp_path): + """The second network is the first shifted by 0.1. - extracted = nmr.extract(obs) + A crossed model column would put that shift on the wrong name. + """ + values = node_series(sample_network, nodes=NODE_IDS) + shifted = modified_network(tmp_path, offset=0.1) + obs = NodeObservation(values, at="2", item="2", name="Node_2_Obs") + + cmp = ms.match( + obs, + [ + NetworkModelResult(sample_network, item="WaterLevel", name="Network_1"), + NetworkModelResult(shifted, item="WaterLevel", name="Network_2"), + ], + ) - assert isinstance(extracted, NodeModelResult) - assert extracted.name == "network_model" - assert extracted.node in nmr.nodes + bias = cmp.score(metric="bias") + assert bias["Network_1"] == pytest.approx(0.0, abs=1e-9) + assert bias["Network_2"] == pytest.approx(0.1, abs=1e-4) + def test_a_matched_breakpoint_scores_zero_against_its_own_series( + self, sample_network + ): + """The break point's data sits on neither of the reach's nodes.""" + values = breakpoint_series(sample_network) + nmr = NetworkModelResult(sample_network, item=REACH_ITEM, name="Network_Model") + obs = NodeObservation(values, at=BREAKPOINT, name="BP_Obs") -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_extract_reach_observation_non_equivalent_breakpoints_raises(sample_node_data): - path_to_file = "./tests/testdata/network.res1d" - network = Network.from_mike(path_to_file) - nmr = NetworkModelResult(network, item="Discharge") - obs_data = sample_node_data.rename(columns={"WaterLevel": "Discharge"}) - obs = ms.ReachObservation(obs_data, reach="113l1", item="Discharge") + cmp = ms.match(obs, nmr) - with pytest.raises(ValueError, match="Not all data in breakpoints are equivalent"): - nmr.extract(obs) + assert cmp.n_points == len(values) + assert cmp.score()["Network_Model"] == pytest.approx(0.0, abs=1e-9) + def test_a_matched_reach_scores_zero_against_its_breakpoints_series( + self, sample_network + ): + values = breakpoint_series(sample_network) + nmr = NetworkModelResult(sample_network, item=REACH_ITEM, name="Network_Model") + obs = ReachObservation(values, reach=REACH, name="Reach_Obs") -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_extract_reach_observation_with_reaches_not_populated_raises_valueerror( - sample_node_data, -): - path_to_file = "./tests/testdata/network.res1d" - network = Network.from_mike(path_to_file, reaches=[]) - nmr = NetworkModelResult(network, item="WaterLevel") - obs = ms.ReachObservation(sample_node_data, reach="100l1", item="WaterLevel") + cmp = ms.match(obs, nmr) - with pytest.raises(ValueError, match="none of its breakpoints have data loaded"): - nmr.extract(obs) + assert cmp.n_points == len(values) + assert cmp.score()["Network_Model"] == pytest.approx(0.0, abs=1e-9) + def test_the_selected_item_brings_its_own_values(self, sample_network_multivars): + """Discharge is ten times WaterLevel here, so the selected column cannot + pass for the one left behind.""" + discharge = node_series(sample_network_multivars, "Pressure") + nmr = NetworkModelResult( + sample_network_multivars, item="Pressure", name="Network_Model" + ) + obs = NodeObservation(discharge, at="11", item="11") -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_extract_reach_observation_breakpoint_node_missing_raises_valueerror( - sample_node_data, -): - path_to_file = "./tests/testdata/network.res1d" - network = Network.from_mike(path_to_file) - nmr = NetworkModelResult(network, item="Discharge") - obs_data = sample_node_data.rename(columns={"WaterLevel": "Discharge"}) - baseline_obs = ms.ReachObservation(obs_data, reach="100l1", item="Discharge") - node_id = nmr.extract(baseline_obs).node - remaining_nodes = [] - for node in nmr.data.node.values: - node_int = int(node) - if node_int != node_id: - remaining_nodes.append(node_int) - nmr.data = nmr.data.sel(node=remaining_nodes) - - obs = ms.ReachObservation(obs_data, reach="100l1", item="Discharge") - - with pytest.raises(ValueError, match="matching breakpoint nodes are missing"): - nmr.extract(obs) + extracted = nmr.extract(obs) + assert extracted.to_dataframe()["Network_Model"].to_numpy() == pytest.approx( + discharge["11"].to_numpy() + ) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_from_mike_nodes_filter_creates_full_network(): - """When nodes is specified, the full network topology is created.""" - path_to_file = "./tests/testdata/network.res1d" - full_network = Network.from_mike(path_to_file) + def test_a_matched_item_scores_zero_against_its_own_series( + self, sample_network_multivars + ): + discharge = node_series(sample_network_multivars, "Pressure") + nmr = NetworkModelResult( + sample_network_multivars, item="Pressure", name="Network_Model" + ) + obs = NodeObservation(discharge, at="11", item="11", name="Node_11_Obs") - selected_nodes = ["1", "108"] - partial_network = Network.from_mike(path_to_file, nodes=selected_nodes) + cmp = ms.match(obs, nmr) - # Full topology is preserved - assert ( - partial_network.graph.number_of_nodes() == full_network.graph.number_of_nodes() - ) + assert cmp.score()["Network_Model"] == pytest.approx(0.0) + def test_an_observations_aux_item_brings_its_own_values(self, sample_network): + """An auxiliary column of the observation reaches the comparer intact.""" + water_level = node_series(sample_network, nodes=NODE_IDS) + nmr = NetworkModelResult( + sample_network, item="WaterLevel", name="Network_Model" + ) + sensor = pd.DataFrame( + { + "WaterLevel": water_level["1"], + "battery": np.arange(len(water_level), dtype=float), + } + ) + obs = NodeObservation( + sensor, at="1", item="WaterLevel", aux_items=["battery"], name="Node_1" + ) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_from_mike_nodes_filter_only_selected_have_data(): - """When nodes is specified, only selected nodes contain non-empty data.""" - path_to_file = "./tests/testdata/network.res1d" + cmp = ms.match(obs, nmr) - selected_nodes = ["1", "108"] - network = Network.from_mike(path_to_file, nodes=selected_nodes, reaches=[]) - g = network.graph.copy() + assert cmp.data["battery"].attrs["kind"] == "aux" + assert cmp.data["battery"].to_numpy() == pytest.approx( + sensor["battery"].to_numpy() + ) + assert cmp.score()["Network_Model"] == pytest.approx(0.0) - n_nodes = network.graph.number_of_nodes() - assert sum([g.nodes[n]["data"].empty for n in g.nodes]) == n_nodes - 2 - for n in selected_nodes: - assert not g.nodes[network.find(n)]["data"].empty + def test_a_reach_observations_aux_item_brings_its_own_values(self, sample_network): + """The break point path carries the observation's auxiliaries too.""" + nmr = NetworkModelResult(sample_network, item=REACH_ITEM, name="Network_Model") + model = breakpoint_series(sample_network)["Discharge"] + sensor = pd.DataFrame( + { + "Discharge": model.to_numpy(), + "battery": np.arange(len(model), dtype=float), + }, + index=model.index, + ) + obs = ms.ReachObservation( + sensor, + reach=REACH, + item="Discharge", + aux_items=["battery"], + name="Reach_100l1", + ) + cmp = ms.match(obs, nmr) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_from_mike_nodes_single_string(): - """nodes argument accepts a single string (not just a list).""" - path_to_file = "./tests/testdata/network.res1d" - full_network = Network.from_mike(path_to_file) + assert cmp.data["battery"].attrs["kind"] == "aux" + assert cmp.data["battery"].to_numpy() == pytest.approx( + sensor["battery"].to_numpy() + ) + assert cmp.score()["Network_Model"] == pytest.approx(0.0, abs=1e-9) - network = Network.from_mike(path_to_file, nodes="108", reaches=[]) - g = network.graph.copy() + def test_an_aux_item_brings_its_own_values(self, sample_network_multivars): + """The aux item rides alongside the scored one, and holds its own series.""" + water_level = node_series(sample_network_multivars, "Head") + discharge = node_series(sample_network_multivars, "Pressure") + nmr = NetworkModelResult( + sample_network_multivars, + item="Head", + aux_items=["Pressure"], + name="Network_Model", + ) + obs = NodeObservation(water_level, at="11", item="11", name="Node_11_Obs") - assert g.number_of_nodes() == full_network.graph.number_of_nodes() + cmp = ms.match(obs, nmr) - nodes_with_data = [n for n in g.nodes if not g.nodes[n]["data"].empty] - nodes_with_data = [network.recall(n)["node"] for n in nodes_with_data] - assert nodes_with_data == ["108"] + assert cmp.data["Pressure"].attrs["kind"] == "aux" + assert cmp.data["Pressure"].to_numpy() == pytest.approx( + discharge["11"].to_numpy() + ) + assert cmp.score()["Network_Model"] == pytest.approx(0.0) + + +@dataclass(frozen=True) +class ResultFile: + """A result file, and locations in it worth scoring. + A named node carries `node_item`; `reach` carries `reach_item` along its + break points, one of which sits at `position`. + """ -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" + path: str + node_item: str + node: str + reach_item: str + reach: str + position: float + + +@pytest.mark.parametrize( + "case", + [ + ResultFile( + path="./tests/testdata/network.res1d", + node_item="WaterLevel", + node="1", + reach_item="Discharge", + reach="100l1", + position=23.8413574216414, + ), + # EPANET results are read together with the .resx companion sitting + # beside them, which mikeio1d finds from the .res path itself. + ResultFile( + path="./tests/testdata/epanet.res", + node_item="Head", + node="10", + reach_item="Flow", + reach="10", + position=0.0, + ), + ], + ids=["res1d", "epanet"], ) -def test_dataframe_from_partial_network(): - """nodes argument accepts a single string (not just a list).""" - path_to_file = "./tests/testdata/network.res1d" - selected_nodes = ["108", "101"] - network = Network.from_mike(path_to_file, nodes=selected_nodes, reaches=[]) - nodes_in_df = network.to_dataframe().droplevel(axis=1, level=1).columns +class TestResultFile: + """The formats NetworkModelResult claims to open, opened.""" - assert set(nodes_in_df) == set([network.find(n) for n in selected_nodes]) + @staticmethod + def _observation_for(mr, item): + """Data shaped for `mr`, to name a location with. Its values go unused.""" + time = pd.date_range(*mr.period, periods=10) + return pd.DataFrame({item: np.arange(len(time), dtype=float)}, index=time) + def test_a_model_result_can_be_built_from_a_result_file(self, case): + mr = NetworkModelResult(case.path, item=case.node_item) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_nodes_filtered_network_keeps_datetime_index(): - """Topology-only nodes must not degrade the time index to object dtype. + assert mr.quantity.name == case.node_item + assert mr.period[0] < mr.period[1] - A nodes-filtered network keeps the full topology, storing empty data for - the unselected nodes. Concatenating those empty (RangeIndex) frames must - not corrupt the DatetimeIndex, otherwise ms.match() later fails with - "time must be datetime". - """ - path_to_file = "./tests/testdata/network.res1d" - network = Network.from_mike(path_to_file, nodes=["108", "101"], reaches=[]) + def test_model_result_recognises_the_result_file(self, case): + mr = ms.model_result(case.path, item=case.node_item) - assert isinstance(network._df.index, pd.DatetimeIndex) - assert network._df.index.dtype == "datetime64[ns]" - assert network.to_dataset()["time"].dtype == np.dtype("datetime64[ns]") + assert isinstance(mr, NetworkModelResult) + def test_a_model_result_carries_the_files_own_values(self, case): + """Checked against mikeio1d's own read of the file. -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_from_res1d_empty_nodes_and_reaches_keeps_topology_and_empty_outputs(): - path_to_file = "./tests/testdata/network.res1d" - network = Network.from_mike(path_to_file, nodes=["108", "101"], reaches=[]) + That read goes straight to the result file, not through the Network the + model result is built on, so the two agreeing pins the whole way in. + """ + expected = mikeio1d.open(case.path).nodes.read()[ + f"{case.node_item}:{case.node}" + ] + mr = NetworkModelResult(case.path, item=case.node_item, name="Network_Model") + obs = NodeObservation(expected, at=case.node, name="Node_Obs") - assert isinstance(network._df.index, pd.DatetimeIndex) - assert network._df.index.dtype == "datetime64[ns]" - assert network.to_dataset()["time"].dtype == np.dtype("datetime64[ns]") + extracted = mr.extract(obs) + assert extracted.to_dataframe()["Network_Model"].to_numpy() == pytest.approx( + expected.to_numpy() + ) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -def test_from_mike_empty_nodes_and_reaches_keeps_topology_and_empty_outputs(): - path_to_file = "./tests/testdata/network.res1d" - full_network = Network.from_mike(path_to_file) - network = Network.from_mike(path_to_file, nodes=[], reaches=[]) + def test_a_named_node_extracts_at_that_node(self, case): + mr = NetworkModelResult(case.path, item=case.node_item) + obs = NodeObservation(self._observation_for(mr, case.node_item), at=case.node) - assert network.graph.number_of_nodes() == full_network.graph.number_of_nodes() + extracted = mr.extract(obs) - df = network.to_dataframe() - assert df.empty - assert isinstance(df.columns, pd.MultiIndex) - assert df.columns.names == ["node", "quantity"] - assert df.index.name == "time" + assert extracted.node == case.node - ds = network.to_dataset() - assert isinstance(ds, xr.Dataset) - assert len(ds.data_vars) == 0 + def test_a_break_point_extracts_at_the_networks_own_position(self, case): + mr = NetworkModelResult(case.path, item=case.reach_item) + obs = NodeObservation( + self._observation_for(mr, case.reach_item), at=(case.reach, case.position) + ) + extracted = mr.extract(obs) -# --------------------------------------------------------------------------- -# Optional reach length -# --------------------------------------------------------------------------- + assert extracted.at == (case.reach, case.position) + def test_a_reach_extracts_to_one_of_its_break_points(self, case): + mr = NetworkModelResult(case.path, item=case.reach_item, name="network_model") + obs = ms.ReachObservation( + self._observation_for(mr, case.reach_item), reach=case.reach + ) -class _StubBreakPoint(ReachBreakPoint): - """Minimal concrete ReachBreakPoint for building reaches by hand.""" + extracted = mr.extract(obs) - def __init__(self, reach_id, distance, data=None): - self._id = (reach_id, distance) - self._data = pd.DataFrame() if data is None else data + assert extracted.name == "network_model" + reach, position = extracted.at + assert reach == case.reach + assert position == pytest.approx(case.position) - @property - def id(self): - return self._id - @property - def data(self): - return self._data +def test_extract_reach_observation_non_equivalent_breakpoints_raises(sample_node_data): + path_to_file = "./tests/testdata/network.res1d" + network = Network.open(path_to_file) + nmr = NetworkModelResult(network, item="Discharge") + obs_data = sample_node_data.rename(columns={"WaterLevel": "Discharge"}) + obs = ms.ReachObservation(obs_data, reach="113l1", item="Discharge") + with pytest.raises(ValueError, match="Not all data in breakpoints are equivalent"): + nmr.extract(obs) -def _two_node_pair(): - time = pd.date_range("2020", periods=3, freq="h") - df = pd.DataFrame({"WaterLevel": [1.0, 1.1, 1.2]}, index=time) - return BasicNode("a", df), BasicNode("b", df.copy()) +def test_extract_reach_whose_breakpoints_carry_no_values_raises_valueerror( + sample_node_data, tmp_path +): + """A break point can name a quantity and hold nothing for it. -class TestOptionalReachLength: - """Reach length is undefined in some domains, so it must be omittable.""" + The sibling case, a reach whose break points never named the quantity, is + covered by the test below; the network is what tells the two apart. + """ + blank = modified_network(tmp_path, blank=True) + nmr = NetworkModelResult(blank, item="WaterLevel") + obs = ms.ReachObservation(sample_node_data, reach=REACH, item="WaterLevel") - def test_subclass_may_omit_length(self): - class LengthlessReach(NetworkReach): - def __init__(self, id, start, end): - self._id, self._start, self._end = id, start, end + with pytest.raises(ValueError, match="none of them carry values for it"): + nmr.extract(obs) - @property - def id(self): - return self._id - @property - def start(self): - return self._start +def test_extract_reach_whose_breakpoints_do_not_carry_the_quantity_raises_valueerror( + sample_node_data, +): + """Head is an EPANET node quantity, so no break point of a pipe carries it.""" + nmr = NetworkModelResult(EPANET, item="Head") + obs = ms.ReachObservation(sample_node_data, reach="10", item="WaterLevel") - @property - def end(self): - return self._end + with pytest.raises(ValueError, match="none of its breakpoints carry quantity"): + nmr.extract(obs) - @property - def breakpoints(self): - return [] - a, b = _two_node_pair() - reach = LengthlessReach("r1", a, b) +def test_extract_reach_not_in_the_network_raises_keyerror( + sample_network, sample_node_data +): + nmr = NetworkModelResult(sample_network, item=REACH_ITEM) + obs = ms.ReachObservation(sample_node_data, reach="no_such_reach") - assert reach.length is None - assert Network([reach]).graph.number_of_nodes() == 2 + with pytest.raises(KeyError, match="no_such_reach"): + nmr.extract(obs) - def test_basic_reach_length_defaults_to_none(self): - a, b = _two_node_pair() - assert BasicReach("r1", a, b).length is None +def test_extract_breakpoint_without_data_for_the_quantity_raises_valueerror( + sample_node_data, +): + """MIKE 1D stores WaterLevel and Discharge at alternating grid points, so a + breakpoint that exists can still hold nothing for the selected quantity.""" + network = Network.open("./tests/testdata/network.res1d") + nmr = NetworkModelResult(network, item="WaterLevel") + obs = ms.NodeObservation(sample_node_data, at=("94l1", 21.285), item="WaterLevel") - def test_edge_length_is_none_when_undefined(self): - a, b = _two_node_pair() + with pytest.raises(ValueError, match="no data for quantity 'WaterLevel'"): + nmr.extract(obs) - network = Network([BasicReach("r1", a, b)]) - assert [d["length"] for *_, d in network.graph.edges(data=True)] == [None] +class TestNodeObservationAliases: + """NodeObservation accepts a node name or a (reach, position) tuple.""" - def test_breakpoint_distances_survive_an_undefined_length(self): - """Only the final segment needs the total, so the rest keep real lengths.""" - a, b = _two_node_pair() - breakpoints = [_StubBreakPoint("r1", d) for d in (30.0, 70.0)] + @pytest.mark.parametrize("at", [42, np.int64(42)]) + def test_an_integer_is_refused(self, sample_node_data, at): + with pytest.raises(TypeError, match="not an integer"): + NodeObservation(sample_node_data, at=at) - network = Network([BasicReach("r1", a, b, breakpoints=breakpoints)]) + def test_string_alias_stored(self, sample_node_data): + obs = NodeObservation(sample_node_data, at="node_A", name="test") + assert obs.at == "node_A" + assert isinstance(obs.at, str) + assert obs.gtype == "node" - lengths = sorted( - (d["length"] for *_, d in network.graph.edges(data=True)), - key=lambda v: (v is None, v), - ) - assert lengths == [30.0, 40.0, None] + def test_a_named_node_knows_its_node(self, sample_node_data): + obs = NodeObservation(sample_node_data, at="node_A") + assert obs.node == "node_A" - def test_length_weighted_algorithms_fail_loudly(self): - """Storing None keeps networkx honest. + def test_tuple_node_stored(self, sample_node_data): + obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) + assert obs.at == ("reach_1", 24.5) + assert isinstance(obs.at, tuple) + assert obs.gtype == "node" - Omitting the attribute instead would let networkx default the weight to - 1, so every call below would return a plausible but meaningless number. - With None, shortest-path treats the edge as hidden and the arithmetic - consumers raise. - """ - import networkx as nx + def test_a_breakpoint_has_no_node_name(self, sample_node_data): + """`node` names a junction; a breakpoint is placed along a reach instead.""" + obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) + assert obs.node is None - a, b = _two_node_pair() - g = Network([BasicReach("r1", a, b)]).graph + def test_a_breakpoint_survives_trimming(self, sample_node_data): + obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) - with pytest.raises(nx.NetworkXNoPath): - nx.shortest_path_length(g, 0, 1, weight="length") + trimmed = obs.trim(start_time=obs.time[1], end_time=obs.time[-1]) - with pytest.raises(TypeError): - g.size(weight="length") + assert trimmed.at == ("reach_1", 24.5) + assert len(trimmed) == len(obs) - 1 - def test_known_length_is_unchanged(self): - a, b = _two_node_pair() - breakpoints = [_StubBreakPoint("r1", 40.0)] + def test_a_position_tol_survives_trimming(self, sample_node_data): + obs = NodeObservation(sample_node_data, at=("reach_1", 24.5), position_tol=2.0) - network = Network([BasicReach("r1", a, b, 100.0, breakpoints)]) + trimmed = obs.trim(start_time=obs.time[1], end_time=obs.time[-1]) - assert sorted(d["length"] for *_, d in network.graph.edges(data=True)) == [ - 40.0, - 60.0, - ] + assert trimmed.position_tol == 2.0 + @pytest.mark.parametrize("tol", [-1.0, np.inf, np.nan]) + def test_an_invalid_position_tol_is_refused(self, sample_node_data, tol): + with pytest.raises(ValueError, match="position_tol"): + NodeObservation(sample_node_data, at=("reach_1", 24.5), position_tol=tol) -# --------------------------------------------------------------------------- -# Which extensions each constructor accepts, and why the rest are refused -# --------------------------------------------------------------------------- + def test_a_named_node_observation_survives_trimming(self, sample_node_data): + obs = NodeObservation(sample_node_data, at="node_A") + trimmed = obs.trim(start_time=obs.time[1], end_time=obs.time[-1]) -@pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) -class TestExtensionPolicy: - @pytest.mark.parametrize("suffix", [".res1d", ".res11", ".RES1D"]) - def test_from_mike_accepts_mike_extensions(self, tmp_path, suffix): - """The file does not exist, so mikeio1d - not the guard - is what complains.""" - with pytest.raises((FileExistsError, FileNotFoundError)): - Network.from_mike(tmp_path / f"network{suffix}") - - def test_error_lists_only_readable_extensions(self): - with pytest.raises(NotImplementedError) as excinfo: - Network.from_mike("network.nc") - - message = str(excinfo.value) - for extension in _MIKE_EXTENSIONS | _EPANET_EXTENSIONS: - assert extension in message - for extension in _UNSUPPORTED_EXTENSIONS: - assert extension not in message - - def test_swmm_refusal_names_the_companion_inp(self): - """A real file, so this fails the day SWMM support lands.""" - with pytest.raises(NotImplementedError, match=r"companion '\.inp'"): - Network.from_mike("./tests/testdata/swmm.out") - - def test_resx_refusal_points_at_the_resx_argument(self): - """'.resx' is a companion, so the message must name what to do instead.""" - with pytest.raises( - NotImplementedError, match=r"from_epanet\(res, resx=\.\.\.\)" - ): - Network.from_mike("./tests/testdata/epanet.resx") + assert trimmed.at == "node_A" + assert len(trimmed) == len(obs) - 1 - @pytest.mark.parametrize("suffix", [".prf", ".crf", ".xrf", ".whr"]) - def test_formats_without_a_fixture_are_refused(self, tmp_path, suffix): - with pytest.raises(NotImplementedError, match="no test fixture"): - Network.from_mike(tmp_path / f"network{suffix}") - def test_every_mikeio1d_extension_is_accounted_for(self): - """A new mikeio1d format must be read or explicitly refused, never ignored.""" - from mikeio1d import Res1D +class TestReuseOfAValidatedDataset: + """Data that has been through modelskill already knows where it sits. - accounted_for = ( - _MIKE_EXTENSIONS | _EPANET_EXTENSIONS | set(_UNSUPPORTED_EXTENSIONS) - ) + The constructors cannot re-place it, so a location that disagrees with the + one the data carries is refused rather than dropped. + """ - assert accounted_for == Res1D.get_supported_file_extensions() + def test_the_location_the_data_carries_is_accepted(self, sample_node_data): + obs = NodeObservation(sample_node_data, at="123", item="WaterLevel") - def test_res1d_opened_with_a_path_is_refused(self): - """mikeio1d calls str.endswith on file_path, so a Path breaks it later on.""" - from mikeio1d import Res1D + rebuilt = NodeObservation(obs.data, at="123") - res = Res1D(Path("./tests/testdata/network.res1d")) + assert rebuilt.at == "123" - with pytest.raises(TypeError, match="file_path"): - Network.from_mike(res) + def test_a_conflicting_node_is_refused(self, sample_node_data): + obs = NodeObservation(sample_node_data, at="123", item="WaterLevel") + with pytest.raises(ValueError, match="already sits at"): + NodeObservation(obs.data, at="456") -# --------------------------------------------------------------------------- -# NodeObservation — alias / breakpoint node forms -# --------------------------------------------------------------------------- + def test_a_break_point_rebuilt_at_its_own_position_is_accepted( + self, sample_node_data + ): + obs = NodeObservation(sample_node_data, at=("r1", 50.0), item="WaterLevel") + rebuilt = NodeObservation(obs.data, at=("r1", 50.0)) -class TestNodeObservationAliases: - """NodeObservation accepts int, str alias, and (reach, distance) tuple.""" + assert rebuilt.at == ("r1", 50.0) - def test_integer_node_unchanged(self, sample_node_data): - obs = NodeObservation(sample_node_data, at=42) - assert obs.at == 42 - assert isinstance(obs.at, int) + def test_a_conflicting_break_point_is_refused(self, sample_node_data): + obs = NodeObservation(sample_node_data, at=("r1", 50.0), item="WaterLevel") - def test_integer_node_coord(self, sample_node_data): - obs = NodeObservation(sample_node_data, at=42) - assert "node" in obs.data.coords - assert int(obs.data.coords["node"].item()) == 42 + with pytest.raises(ValueError, match="already sits at"): + NodeObservation(obs.data, at=("r1", 75.0)) - def test_string_alias_stored(self, sample_node_data): - obs = NodeObservation(sample_node_data, at="node_A", name="test") - assert obs.at == "node_A" - assert isinstance(obs.at, str) + def test_data_with_no_network_location_is_refused(self, sample_node_data): + """A point observation knows where it sits, but not as a network does.""" + obs = ms.PointObservation(sample_node_data, x=0.0, y=0.0, item="WaterLevel") - def test_string_alias_has_node_coord(self, sample_node_data): - obs = NodeObservation(sample_node_data, at="node_A") - assert "node" in obs.data.coords - assert obs.data.coords["node"].item() == "node_A" + with pytest.raises(ValueError, match="no network location"): + NodeObservation(obs.data, at="123") - def test_string_alias_gtype_is_node(self, sample_node_data): - obs = NodeObservation(sample_node_data, at="node_A") - assert obs.data.attrs["gtype"] == "node" + def test_the_reach_the_data_carries_is_accepted(self, sample_node_data): + obs = ReachObservation(sample_node_data, reach="r1", item="WaterLevel") - def test_tuple_node_stored(self, sample_node_data): - obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) - assert obs.at == ("reach_1", 24.5) - assert isinstance(obs.at, tuple) + rebuilt = ReachObservation(obs.data, reach="r1") - def test_tuple_node_gtype_is_node(self, sample_node_data): - obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) - assert obs.data.attrs["gtype"] == "node" + assert rebuilt.reach == "r1" - def test_tuple_node_has_reach_distance_coords(self, sample_node_data): - obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) - assert "reach" in obs.data.coords - assert "distance" in obs.data.coords - assert str(obs.data.coords["reach"].item()) == "reach_1" - assert float(obs.data.coords["distance"].item()) == pytest.approx(24.5) + def test_a_conflicting_reach_is_refused(self, sample_node_data): + obs = ReachObservation(sample_node_data, reach="r1", item="WaterLevel") - def test_tuple_node_has_no_node_coord(self, sample_node_data): - obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) - assert "node" not in obs.data.coords + with pytest.raises(ValueError, match="already sits at"): + ReachObservation(obs.data, reach="r2") - def test_tuple_node_roundtrip_via_create_new_instance(self, sample_node_data): - obs = NodeObservation(sample_node_data, at=("reach_1", 24.5)) - obs2 = obs._create_new_instance(obs.data) - assert obs2.at == ("reach_1", 24.5) + def test_a_break_points_data_is_not_reach_level(self, sample_node_data): + """A chainage is a narrower claim than the reach it lies on.""" + obs = NodeObservation(sample_node_data, at=("r1", 50.0), item="WaterLevel") - def test_string_roundtrip_via_create_new_instance(self, sample_node_data): - obs = NodeObservation(sample_node_data, at="node_A") - obs2 = obs._create_new_instance(obs.data) - assert obs2.at == "node_A" + with pytest.raises(ValueError, match="chainage"): + ReachObservation(obs.data, reach="r1") # --------------------------------------------------------------------------- @@ -891,524 +935,269 @@ def test_string_roundtrip_via_create_new_instance(self, sample_node_data): class TestNetworkModelResultAliasResolution: - """NetworkModelResult.extract() resolves str and tuple aliases via alias_map.""" + """extract() resolves a node name or a (reach, position) pair to a location.""" + + def test_the_network_is_kept_as_given(self, sample_network): + nmr = NetworkModelResult(sample_network, item="WaterLevel") - def test_network_stored(self, sample_network): - nmr = NetworkModelResult(sample_network) - assert hasattr(nmr, "network") - assert "123" in nmr.network._alias_map - assert "456" in nmr.network._alias_map - assert "789" in nmr.network._alias_map + assert nmr.network is sample_network def test_extract_with_string_alias(self, sample_network, sample_node_data): - nmr = NetworkModelResult(sample_network) - obs = NodeObservation(sample_node_data, at="123", name="Node_123") + nmr = NetworkModelResult(sample_network, item="WaterLevel") + obs = NodeObservation(sample_node_data, at="1", name="Node_1") + extracted = nmr.extract(obs) - expected_id = sample_network.find(node="123") - assert isinstance(extracted, NodeModelResult) - assert extracted.node == expected_id + + assert extracted.node == "1" def test_extract_string_alias_wrong_key_raises( self, sample_network, sample_node_data ): - nmr = NetworkModelResult(sample_network) + nmr = NetworkModelResult(sample_network, item="WaterLevel") obs = NodeObservation(sample_node_data, at="nonexistent_node") + with pytest.raises(ValueError, match="not found"): nmr.extract(obs) def test_extract_with_tuple_breakpoint(self, sample_network, sample_node_data): - """Tuple alias is resolved via _alias_map (mapping injected for this test).""" - nmr = NetworkModelResult(sample_network) - existing_int = int(sample_network.find(node="123")) - nmr.network._alias_map[("reach_test", 10.0)] = existing_int - obs = NodeObservation(sample_node_data, at=("reach_test", 10.0)) + nmr = NetworkModelResult(sample_network, item=REACH_ITEM) + obs = NodeObservation(sample_node_data, at=BREAKPOINT) + extracted = nmr.extract(obs) - assert extracted.node == existing_int + + assert extracted.at == BREAKPOINT def test_extract_with_tuple_breakpoint_tolerance( self, sample_network, sample_node_data ): - nmr = NetworkModelResult(sample_network) - existing_int = int(sample_network.find(node="123")) - base_distance = 10.0 - tol = NetworkModelResult._CHAINAGE_TOLERANCE - nmr.network._alias_map[("reach_test", base_distance)] = existing_int - obs = NodeObservation( - sample_node_data, at=("reach_test", base_distance + tol / 2) - ) + nmr = NetworkModelResult(sample_network, item=REACH_ITEM) + obs = NodeObservation(sample_node_data, at=(REACH, POSITION + 5e-4)) + extracted = nmr.extract(obs) - assert extracted.node == existing_int + + # The position recorded is the network's own, not the one typed. + assert extracted.at == BREAKPOINT def test_extract_with_tuple_breakpoint_outside_tolerance_raises( self, sample_network, sample_node_data ): - nmr = NetworkModelResult(sample_network) - existing_int = int(sample_network.find(node="123")) - base_distance = 10.0 - tol = NetworkModelResult._CHAINAGE_TOLERANCE - nmr.network._alias_map[("reach_test", base_distance)] = existing_int - obs = NodeObservation( - sample_node_data, at=("reach_test", base_distance + tol + 1e-4) - ) + nmr = NetworkModelResult(sample_network, item=REACH_ITEM) + obs = NodeObservation(sample_node_data, at=(REACH, 50.0 + 2e-3)) + with pytest.raises(ValueError, match="not found"): nmr.extract(obs) - def test_extract_with_tuple_breakpoint_uses_closest_within_tolerance( + def test_extract_with_a_wider_position_tol_snaps_to_the_breakpoint( self, sample_network, sample_node_data ): - nmr = NetworkModelResult(sample_network) - base_distance = 10.0 - tol = NetworkModelResult._CHAINAGE_TOLERANCE - node_a = int(sample_network.find(node="123")) - node_b = int(sample_network.find(node="456")) - nmr.network._alias_map[("reach_test", base_distance + 2e-4)] = node_a - nmr.network._alias_map[("reach_test", base_distance + 8e-4)] = node_b - + nmr = NetworkModelResult(sample_network, item=REACH_ITEM) obs = NodeObservation( - sample_node_data, at=("reach_test", base_distance + tol * 0.6) + sample_node_data, at=(REACH, POSITION - 3.0), position_tol=5.0 ) + extracted = nmr.extract(obs) - assert extracted.node == node_b - def test_extract_with_tuple_breakpoint_tie_uses_smallest_node_id( + assert extracted.at == BREAKPOINT + + def test_extract_snaps_past_a_closer_breakpoint_that_lacks_the_item( self, sample_network, sample_node_data ): - nmr = NetworkModelResult(sample_network) - base_distance = 10.0 - tol = NetworkModelResult._CHAINAGE_TOLERANCE - node_a = int(sample_network.find(node="123")) - node_b = int(sample_network.find(node="456")) - nmr.network._alias_map[("reach_test", base_distance + 4e-4)] = node_a - nmr.network._alias_map[("reach_test", base_distance + 8e-4)] = node_b - + """WaterLevel and Discharge alternate along a reach, so the nearest + breakpoint to a chainage may not carry the item being scored.""" + nmr = NetworkModelResult(sample_network, item="WaterLevel") obs = NodeObservation( - sample_node_data, at=("reach_test", base_distance + tol * 0.6) + sample_node_data, at=(REACH, POSITION + 6.0), position_tol=20.0 ) + extracted = nmr.extract(obs) - assert extracted.node == min(node_a, node_b) + + assert extracted.at == sample_network.addresses(reach=REACH)[-1] + + def test_extract_outside_a_wider_position_tol_raises( + self, sample_network, sample_node_data + ): + nmr = NetworkModelResult(sample_network, item=REACH_ITEM) + obs = NodeObservation( + sample_node_data, at=(REACH, POSITION - 3.0), position_tol=1.0 + ) + + with pytest.raises(ValueError, match="not found.*position_tol"): + nmr.extract(obs) def test_extract_tuple_alias_wrong_key_raises( self, sample_network, sample_node_data ): - nmr = NetworkModelResult(sample_network) + nmr = NetworkModelResult(sample_network, item="WaterLevel") obs = NodeObservation(sample_node_data, at=("nonexistent_reach", 0.0)) + with pytest.raises(ValueError, match="not found"): nmr.extract(obs) - def test_match_with_string_alias(self, sample_network, sample_node_data): - """Full ms.match() workflow works end-to-end with a string alias.""" - nmr = NetworkModelResult(sample_network, name="Network_Model") - obs = NodeObservation(sample_node_data, at="123", name="Node_123") - comparer = ms.match(obs, nmr) - assert comparer.n_points > 0 - assert "Network_Model" in comparer.mod_names - - -# --------------------------------------------------------------------------- -# Res1D adapter — no mikeio1d required, the adapter is duck-typed -# --------------------------------------------------------------------------- - -class _StubLocation: - """Stands in for a mikeio1d ResultNode / ResultGridPoint.""" +# ======================== location identity ======================== - def __init__(self, quantities, df=None): - self.quantities = quantities - self._df = df - def to_dataframe(self): - if self._df is None: - raise AssertionError("to_dataframe() should not be called") - return self._df +class TestLocationIdentity: + """A network timeseries is identified by the name its network gave it.""" + def test_breakpoint_observation_converts_to_a_dataframe(self, sample_node_data): + obs = ms.NodeObservation(sample_node_data, at=("r1", 24.5), item="WaterLevel") -class TestSimplifyColnames: - def test_location_without_quantities_gives_empty_frame(self): - """MIKE 11 keeps its data on gridpoints, leaving nodes with no quantities.""" - df = _simplify_colnames(_StubLocation(quantities=[])) - - assert df.empty - assert list(df.columns) == [] - - def test_quantity_columns_are_stripped_of_location_suffix(self): - time = pd.date_range("2020", periods=2, freq="h") - raw = pd.DataFrame({"WaterLevel:node_1": [1.0, 2.0]}, index=time) - - df = _simplify_colnames(_StubLocation(quantities=["WaterLevel"], df=raw)) + df = obs.to_dataframe() assert list(df.columns) == ["WaterLevel"] + assert len(df) == len(sample_node_data) + def test_reach_observation_converts_to_a_dataframe(self, sample_node_data): + obs = ms.ReachObservation(sample_node_data, reach="r1", item="WaterLevel") -class _StubReach: - """Stands in for a mikeio1d ResultReach.""" - - def __init__(self, name="r1", start_node="a", end_node="b", length=100.0): - self.name = name - self.start_node = start_node - self.end_node = end_node - self.length = length - self.gridpoints = [] - - -class TestRes1DReachConnectivity: - """Formats that expose no reach connectivity must fail with a clear message.""" - - @pytest.mark.parametrize("missing", ["start_node", "end_node"]) - def test_missing_node_raises(self, missing): - reach = _StubReach(**{missing: None}) - - with pytest.raises(ValueError, match="no start/end node for reach 'r1'"): - Res1DReach(reach, Res1DNode("a"), Res1DNode("b")) - - def test_both_nodes_missing_raises(self): - """.resx reports None for both, which the identity checks alone would allow.""" - reach = _StubReach(start_node=None, end_node=None) - - with pytest.raises(ValueError, match="no start/end node"): - Res1DReach(reach, Res1DNode(None), Res1DNode(None)) # type: ignore[arg-type] - - def test_mismatched_start_node_still_raises(self): - with pytest.raises(ValueError, match="Incorrect starting node"): - Res1DReach(_StubReach(), Res1DNode("wrong"), Res1DNode("b")) + df = obs.to_dataframe() + assert list(df.columns) == ["WaterLevel"] -class TestRes1DReachLength: - """mikeio1d returns 0 when it cannot read a length; that is not a real zero.""" - - @pytest.mark.parametrize("reported", [0, 0.0]) - def test_zero_becomes_undefined(self, reported): - reach = Res1DReach(_StubReach(length=reported), Res1DNode("a"), Res1DNode("b")) - - assert reach.length is None - - def test_real_length_passes_through(self): - reach = Res1DReach(_StubReach(length=47.5), Res1DNode("a"), Res1DNode("b")) - - assert reach.length == 47.5 - - -# --------------------------------------------------------------------------- -# from_mike / from_epanet -# --------------------------------------------------------------------------- - -requires_mikeio1d = pytest.mark.skipif( - sys.version_info >= (3, 14), reason="mikeio1d requires Python < 3.14" -) - - -@requires_mikeio1d -class TestFromMike: - def test_res1d(self): - network = Network.from_mike("./tests/testdata/network.res1d") - - assert network.graph.number_of_nodes() == 259 - - def test_res11(self): - """MIKE 11 keeps its data on gridpoints, so its nodes are empty.""" - network = Network.from_mike("./tests/testdata/network_cali.res11") - - assert len(network._reaches) == 3 - assert network.graph.number_of_nodes() == 71 - assert set(network.quantities) == {"Discharge", "Water Level"} - assert [r.n_breakpoints for r in network._reaches.values()] == [23, 21, 23] - - def test_res11_reaches_have_real_lengths(self): - network = Network.from_mike("./tests/testdata/network_cali.res11") - - lengths = [d["length"] for *_, d in network.graph.edges(data=True)] - assert all(length > 0 for length in lengths) - - def test_open_res1d_object(self): - from mikeio1d import Res1D - - res = Res1D("./tests/testdata/network.res1d") - - network = Network.from_mike(res, nodes=[], reaches=[]) - - assert network.graph.number_of_nodes() == 259 - - def test_epanet_file_is_redirected(self): - with pytest.raises(ValueError, match=r"Use Network\.from_epanet\(\)"): - Network.from_mike("./tests/testdata/epanet.res") - - def test_unknown_extension(self): - with pytest.raises(NotImplementedError, match="Unsupported file extension"): - Network.from_mike("./tests/testdata/obs.dfs0") - - def test_unsupported_type(self): - with pytest.raises(TypeError, match="Expected a str, Path or Res1D object"): - Network.from_mike(42) # type: ignore[arg-type] - - -@requires_mikeio1d -class TestFromEpanet: - def test_epanet(self): - network = Network.from_epanet("./tests/testdata/epanet.res") - - assert network.graph.number_of_nodes() == 11 - assert len(network._reaches) == 13 - assert set(network.quantities) == { - "Demand", - "Head", - "Pressure", - "WaterQuality", - } - assert not network.to_dataframe().empty - - def test_link_node_reaches_have_no_length_or_breakpoints(self): - """Without inp=, mikeio1d reports neither - documented in the docstring.""" - network = Network.from_epanet("./tests/testdata/epanet.res") - - lengths = [d["length"] for *_, d in network.graph.edges(data=True)] - assert lengths and all(length is None for length in lengths) - assert all(r.n_breakpoints == 0 for r in network._reaches.values()) - - def test_reach_observation_cannot_be_matched(self, sample_node_data): - """Follows from having no breakpoints; also documented in the docstring.""" - network = Network.from_epanet("./tests/testdata/epanet.res") - nmr = NetworkModelResult(network, item="Pressure") - obs = ms.ReachObservation(sample_node_data, reach="10", item="WaterLevel") - - with pytest.raises(ValueError, match="breakpoints"): - nmr.extract(obs) - - def test_mike_file_is_redirected(self): - with pytest.raises(ValueError, match=r"Use Network\.from_mike\(\)"): - Network.from_epanet("./tests/testdata/network.res1d") - - def test_open_res1d_object_is_validated(self): - from mikeio1d import Res1D - - res = Res1D("./tests/testdata/network.res1d") - - with pytest.raises(ValueError, match=r"Use Network\.from_mike\(\)"): - Network.from_epanet(res) - - @pytest.mark.parametrize("suffix", [".res", ".RES"]) - def test_extension_is_case_insensitive(self, tmp_path, suffix): - with pytest.raises((FileExistsError, FileNotFoundError)): - Network.from_epanet(tmp_path / f"network{suffix}") - - -# --------------------------------------------------------------------------- -# EPANET companion files: .inp for reach lengths, .resx for extra quantities -# --------------------------------------------------------------------------- - -_EPANET_RES = "./tests/testdata/epanet.res" -_EPANET_RESX = "./tests/testdata/epanet.resx" -_EPANET_INP = "./tests/testdata/epanet.inp" - -# The 12 [PIPES] entries; reach "9" is the pump, which carries no length. -_PUMP_REACH = "9" - - -@requires_mikeio1d -class TestEpanetCompanionInp: - """`.inp` is the only one of the three files carrying reach lengths.""" - - def test_pipe_reaches_get_real_lengths(self): - network = Network.from_epanet(_EPANET_RES, inp=_EPANET_INP) - - lengths = {r.id: r.length for r in network._reaches.values()} - assert lengths["10"] == pytest.approx(3209.544) - assert lengths["110"] == pytest.approx(60.96) - - def test_pump_reach_stays_undefined(self): - """[PIPES] is the only section with lengths, so pumps keep None.""" - network = Network.from_epanet(_EPANET_RES, inp=_EPANET_INP) - - lengths = {r.id: r.length for r in network._reaches.values()} - assert lengths[_PUMP_REACH] is None - assert sum(v is None for v in lengths.values()) == 1 - - def test_graph_edges_carry_the_lengths(self): - network = Network.from_epanet(_EPANET_RES, inp=_EPANET_INP) - - lengths = [d["length"] for *_, d in network.graph.edges(data=True)] - assert sum(v is not None for v in lengths) == 12 - - def test_node_ids_overlapping_reach_ids_are_not_confused(self): - """Most IDs here name both a node and a reach, e.g. '9', '10', '21'.""" - network = Network.from_epanet(_EPANET_RES, inp=_EPANET_INP) - - assert set(network._reaches) & set(network._alias_map) # they do overlap - # Reach "10" is 3209.544 long; node "10" is untouched by the length map. - assert network._reaches["10"].length == pytest.approx(3209.544) - node_10 = network.find(node="10") - assert "Head" in network.to_dataframe()[node_10].columns - - def test_wrong_suffix_is_refused(self): - with pytest.raises(ValueError, match=r"Expected an EPANET '\.inp'"): - Network.from_epanet(_EPANET_RES, inp=_EPANET_RESX) - - def test_file_without_a_pipes_section_is_refused(self, tmp_path): - other = tmp_path / "not-epanet.inp" - other.write_text("[JUNCTIONS]\n;;Name\n9 1000\n") - - with pytest.raises(ValueError, match=r"no \[PIPES\] section"): - Network.from_epanet(_EPANET_RES, inp=other) - - -@requires_mikeio1d -class TestEpanetCompanionResx: - """`.resx` holds extra results for the network defined in the sibling `.res`.""" - - def test_extra_node_quantities_are_merged(self): - network = Network.from_epanet(_EPANET_RES, resx=_EPANET_RESX) - - assert set(network.quantities) == { - "Demand", - "Head", - "Pressure", - "WaterQuality", - "Volume", - "Volume Percentage", - } - - def test_only_the_nodes_present_in_the_resx_gain_them(self): - """The .resx covers the tank and the reservoir, not all eleven nodes.""" - network = Network.from_epanet(_EPANET_RES, resx=_EPANET_RESX) - df = network.to_dataframe() - - with_volume = { - node - for node in df.columns.get_level_values("node").unique() - if "Volume" in df[node].columns - } - # Node IDs are re-indexed to integers, so recall the original labels. - assert {network.recall(node)["node"] for node in with_volume} == {"2", "9"} - - def test_values_come_through(self): - network = Network.from_epanet(_EPANET_RES, resx=_EPANET_RESX) - - reservoir = network.find(node="9") - volume = network.to_dataframe()[(reservoir, "Volume Percentage")] - assert len(volume) == 25 - assert volume.notna().all() - - def test_selective_loading_still_governs_what_is_read(self): - network = Network.from_epanet(_EPANET_RES, resx=_EPANET_RESX, nodes=["2"]) - - df = network.to_dataframe() - tank = network.find(node="2") - assert set(df.columns.get_level_values("node").unique()) == {tank} - assert "Volume" in df[tank].columns - - def test_both_companions_together(self): - network = Network.from_epanet(_EPANET_RES, resx=_EPANET_RESX, inp=_EPANET_INP) - - assert "Volume" in network.quantities - assert network._reaches["10"].length == pytest.approx(3209.544) - - def test_an_open_res1d_object_is_accepted(self): - from mikeio1d import Res1D - - network = Network.from_epanet(_EPANET_RES, resx=Res1D(_EPANET_RESX)) - - assert "Volume" in network.quantities + @pytest.mark.parametrize( + "at, item, node", + [("1", "WaterLevel", "1"), (BREAKPOINT, REACH_ITEM, None)], + ids=["named node", "break point"], + ) + def test_observation_model_result_and_comparer_agree_on_location( + self, sample_network, sample_node_data, at, item, node + ): + nmr = NetworkModelResult(sample_network, item=item, name="Network_Model") + obs = ms.NodeObservation(sample_node_data, at=at, name="Obs") - def test_wrong_suffix_is_refused(self): - with pytest.raises(ValueError, match=r"Expected an EPANET '\.resx'"): - Network.from_epanet(_EPANET_RES, resx=_EPANET_RES) + cmp = ms.match(obs, nmr) + mod = cmp.raw_mod_data["Network_Model"] - def test_a_result_file_of_another_format_is_refused(self): - from mikeio1d import Res1D + assert obs.at == mod.at == cmp.at == at + assert obs.node == mod.node == cmp.node == node - other = Res1D("./tests/testdata/network.res1d") + def test_a_snapped_chainage_stays_on_the_comparer_as_typed( + self, sample_network, sample_node_data + ): + """position_tol snaps the model onto a breakpoint; the comparer and its + raw model result keep the chainage the user gave, so several models with + different grids share it.""" + typed = (REACH, POSITION - 3.0) + nmr = NetworkModelResult(sample_network, item=REACH_ITEM, name="Network_Model") + obs = ms.NodeObservation(sample_node_data, at=typed, position_tol=5.0) - with pytest.raises(ValueError, match=r"Expected an EPANET '\.resx'"): - Network.from_epanet(_EPANET_RES, resx=other) + cmp = ms.match(obs, nmr) - def test_a_companion_from_another_run_is_refused(self, monkeypatch): - """Merging two runs would line up silently and give a wrong network.""" - from mikeio1d import Res1D + assert cmp.at == typed + assert cmp.raw_mod_data["Network_Model"].at == typed - res = Res1D(_EPANET_RES) - resx = Res1D(_EPANET_RESX) - shifted = resx.time_index + pd.Timedelta("1D") + def test_a_matched_breakpoint_records_its_chainage( + self, sample_network, sample_node_data + ): + nmr = NetworkModelResult(sample_network, item=REACH_ITEM, name="Network_Model") + obs = ms.NodeObservation(sample_node_data, at=BREAKPOINT, name="BP") - # Both objects share the Res1D class, so shift only this one instance. - original = type(resx).time_index.fget - monkeypatch.setattr( - type(resx), - "time_index", - property(lambda self: shifted if self is resx else original(self)), - ) + cmp = ms.match(obs, nmr) - with pytest.raises(ValueError, match="does not share a time axis"): - Network.from_epanet(res, resx=resx) + assert cmp.gtype == "node" + assert cmp.node is None + assert cmp.reach == REACH + assert cmp.position == pytest.approx(POSITION) - def test_a_companion_naming_an_unknown_node_is_refused(self, monkeypatch): - """A node the .res has never heard of means these are different models.""" - from mikeio1d import Res1D + def test_a_matched_reach_and_its_model_sit_on_the_reach( + self, sample_network, sample_node_data + ): + """The comparer and its raw model result sit where the observation + sits, the whole reach, whichever breakpoint the model was read from.""" + nmr = NetworkModelResult(sample_network, item=REACH_ITEM, name="Network_Model") + obs = ms.ReachObservation(sample_node_data, reach=REACH, name="Reach") + + cmp = ms.match(obs, nmr) + + assert obs.at == cmp.at == REACH + assert cmp.gtype == "reach" + assert cmp.reach == REACH + assert cmp.position is None + assert cmp.raw_mod_data["Network_Model"].at == REACH + + @pytest.mark.parametrize( + "make_obs, item, location", + [ + (lambda d: ms.NodeObservation(d, at="1"), "WaterLevel", "Location: 1"), + ( + lambda d: ms.NodeObservation(d, at=BREAKPOINT), + REACH_ITEM, + f"Location: {BREAKPOINT}", + ), + ( + lambda d: ms.ReachObservation(d, reach=REACH), + REACH_ITEM, + f"Location: {REACH}", + ), + ], + ids=["named node", "break point", "reach"], + ) + def test_comparer_repr_shows_its_location( + self, sample_network, sample_node_data, make_obs, item, location + ): + nmr = NetworkModelResult(sample_network, item=item, name="Network_Model") - res = Res1D(_EPANET_RES) - resx = Res1D(_EPANET_RESX) - strangers = dict(resx.nodes) | {"not_in_the_res": None} + cmp = ms.match(make_obs(sample_node_data), nmr) - original = type(resx).nodes.fget - monkeypatch.setattr( - type(resx), - "nodes", - property(lambda self: strangers if self is resx else original(self)), - ) + assert location in repr(cmp).splitlines() - with pytest.raises(ValueError, match="not_in_the_res"): - Network.from_epanet(res, resx=resx) - def test_unsupported_type_is_refused(self): - with pytest.raises(TypeError, match="Expected a str, Path or Res1D object"): - Network.from_epanet(_EPANET_RES, resx=42) # type: ignore[arg-type] +class TestObservationFactory: + """ms.observation() routes the network keywords to the right class.""" + def test_at_gives_a_node_observation(self, sample_node_data): + obs = ms.observation(sample_node_data, at="123", item="WaterLevel") -class TestReadInp: - """Minimal .inp reader - see modelskill/model/adapters/_inp.py.""" + assert isinstance(obs, ms.NodeObservation) + assert obs.at == "123" - def _write(self, tmp_path, text): - path = tmp_path / "model.inp" - path.write_text(text) - return path + def test_a_breakpoint_tuple_gives_a_node_observation(self, sample_node_data): + obs = ms.observation(sample_node_data, at=("r1", 24.5), item="WaterLevel") - def test_sections_are_keyed_without_brackets_and_upper_cased(self, tmp_path): - path = self._write(tmp_path, "[Pipes]\n1 a b 10\n[TANKS]\n2 5\n") + assert isinstance(obs, ms.NodeObservation) + assert obs.at == ("r1", 24.5) - assert set(read_sections(path)) == {"PIPES", "TANKS"} + def test_reach_gives_a_reach_observation(self, sample_node_data): + obs = ms.observation(sample_node_data, reach="r1", item="WaterLevel") - def test_comment_and_blank_lines_are_dropped(self, tmp_path): - path = self._write( - tmp_path, - ";a leading banner\n\n[PIPES]\n" - ";;ID Node1 Node2 Length\n" - ";;-- ----- ----- ------\n" - "1 a b 10\n\n", - ) + assert isinstance(obs, ms.ReachObservation) + assert obs.reach == "r1" - assert read_sections(path) == {"PIPES": [["1", "a", "b", "10"]]} + @pytest.mark.parametrize( + "gtype,kwargs,expected", + [ + ("node", {"at": "123"}, ms.NodeObservation), + ("reach", {"reach": "r1"}, ms.ReachObservation), + ], + ) + def test_gtype_can_be_named_outright( + self, sample_node_data, gtype, kwargs, expected + ): + obs = ms.observation(sample_node_data, gtype=gtype, item="WaterLevel", **kwargs) - def test_trailing_comment_is_stripped_from_a_data_row(self, tmp_path): - path = self._write(tmp_path, "[PIPES]\n1 a b 10 ; the short one\n") + assert isinstance(obs, expected) - assert read_sections(path)["PIPES"] == [["1", "a", "b", "10"]] - def test_rows_before_any_section_are_ignored(self, tmp_path): - path = self._write(tmp_path, "stray row\n[PIPES]\n1 a b 10\n") +class TestModelResultFactory: + """ms.model_result() builds a NetworkModelResult from a Network.""" - assert read_sections(path) == {"PIPES": [["1", "a", "b", "10"]]} + def test_a_network_gives_a_network_model_result(self, sample_network): + mr = ms.model_result(sample_network, item="WaterLevel") - def test_lengths_are_read_from_the_fourth_field(self, tmp_path): - path = self._write(tmp_path, "[PIPES]\n1 a b 10.5 300 100\n") + assert isinstance(mr, NetworkModelResult) - assert read_pipe_lengths(path) == {"1": 10.5} + def test_gtype_can_be_named_outright(self, sample_network): + mr = ms.model_result(sample_network, gtype="network", item="WaterLevel") - def test_a_short_row_raises_rather_than_dropping_a_length(self, tmp_path): - path = self._write(tmp_path, "[PIPES]\n1 a b\n") + assert isinstance(mr, NetworkModelResult) - with pytest.raises(ValueError, match="Cannot read a pipe length"): - read_pipe_lengths(path) - def test_a_repeated_section_header_accumulates(self, tmp_path): - path = self._write( - tmp_path, "[PIPES]\n1 a b 10\n[TANKS]\n2 5\n[PIPES]\n3 c d 20\n" - ) +def test_a_reach_observation_keeps_its_weight_and_attrs(sample_node_data): + obs = ReachObservation( + sample_node_data, reach="r1", weight=2.5, attrs={"source": "test"} + ) - assert read_pipe_lengths(path) == {"1": 10.0, "3": 20.0} + assert obs.weight == 2.5 + assert obs.attrs["source"] == "test" + assert obs.quantity == Quantity.undefined() diff --git a/tests/test_quantity.py b/tests/test_quantity.py index efb6c6ca2..d3226ab1b 100644 --- a/tests/test_quantity.py +++ b/tests/test_quantity.py @@ -7,6 +7,14 @@ def test_str(): assert str(wh) == "Significant wave height [m]" +def test_str_without_unit(): + assert str(ms.Quantity(name="Discharge", unit="")) == "Discharge" + + +def test_str_undefined(): + assert str(ms.Quantity.undefined()) == "Undefined" + + def test_from_EUMType_string(): with pytest.warns(match="unit"): # mikeio.EUMType.Significant_wave_height diff --git a/tests/testdata/README.md b/tests/testdata/README.md index a6f4a71c6..552809f61 100644 --- a/tests/testdata/README.md +++ b/tests/testdata/README.md @@ -10,16 +10,18 @@ These network files come from | File | Format | Used for | |---|---|---| -| `network_cali.res11` | MIKE 11 | `Network.from_mike` coverage for `.res11` | -| `epanet.res` | EPANET | `Network.from_epanet` coverage | -| `epanet.resx` | EPANET (MIKE+) | the `resx=` companion — extra node quantities merged onto the `.res` network | -| `epanet.inp` | EPANET input | the `inp=` companion — real pipe lengths, which the `.res` does not carry | -| `swmm.out` | SWMM | asserting `.out` is refused — its reach connectivity lives in a companion `.inp` we do not read yet (#689) | +| `network_cali.res11` | MIKE 11 | nothing here any more — see below | +| `epanet.res` | EPANET | network tests with several quantities per node | +| `epanet.resx` | EPANET (MIKE+) | extra node quantities, merged onto the `.res` network | +| `swmm.out` | SWMM | nothing here any more — see below | -`epanet.resx` and `epanet.inp` pair with `epanet.res`: same run, same IDs. The -`.resx` node and reach IDs are a strict subset of the `.res` ones, and the `.inp` -`[PIPES]` IDs cover every `.res` reach except the pump. +Reading these formats moved to mikeio1d with the rest of the topology layer +(ADR-013), and the tests that covered it moved with it. The files are kept because +mikeio1d has the same copies. Of them, only `epanet.res` and `epanet.resx` are +read here, besides `network.res1d`. -`swmm.out` is kept without its `.inp` on purpose. It pins the refusal, so the test -fails the day we add SWMM support or a future mikeio1d starts reporting reach -connectivity for it. +`epanet.resx` pairs with `epanet.res`: same run, same IDs. The `.resx` node and +reach IDs are a strict subset of the `.res` ones. + +`swmm.out` is kept without its `.inp` on purpose: the refusal it used to pin is +mikeio1d's now, and the file is the fixture that refusal needs. diff --git a/tests/testdata/epanet.inp b/tests/testdata/epanet.inp deleted file mode 100644 index 2b42e3a37..000000000 --- a/tests/testdata/epanet.inp +++ /dev/null @@ -1,226 +0,0 @@ -;***************************************************** -;* Generated from MIKE+ * -;***************************************************** - -[TITLE] - -[JUNCTIONS] -;------------------------------------------------------ -;ID Elevation Demand Pattern -;------------------------------------------------------ -10 216.408000 -11 216.408000 -12 213.360000 -13 211.836000 -21 213.360000 -22 211.836000 -23 210.312000 -31 213.360000 -32 216.408000 - -[RESERVOIRS] -;------------------------------------------------------ -;ID Head Pattern -;------------------------------------------------------ -9 243.840000 - -[TANKS] -;--------------------------------------------------------------------------------------------------------- -;ID Elev. Init. Min. Max. Diam. MinVol VolCurve Overflow -; Level Level Level -;--------------------------------------------------------------------------------------------------------- -2 259.080000 36.576000 30.480000 45.720000 15.392400 0.000000 * No - -[PIPES] -;----------------------------------------------------------------- -;ID Head Tail Length Diam Rough. Minor CV -; Node Node -;----------------------------------------------------------------- -10 10 11 3209.544000 457.200000 100.000000 0.000000 -11 11 12 1609.344000 355.600000 100.000000 0.000000 -110 2 12 60.960000 457.200000 100.000000 0.000000 -111 11 21 1609.344000 254.000000 100.000000 0.000000 -112 12 22 1609.344000 304.800000 100.000000 0.000000 -113 13 23 1609.344000 203.200000 100.000000 0.000000 -12 12 13 1609.344000 254.000000 100.000000 0.000000 -121 21 31 1609.344000 203.200000 100.000000 0.000000 -122 22 32 1609.344000 152.400000 100.000000 0.000000 -21 21 22 1609.344000 254.000000 100.000000 0.000000 -22 22 23 1609.344000 304.800000 100.000000 0.000000 -31 31 32 1609.344000 152.400000 100.000000 0.000000 - -[VALVES] -;------------------------------------------------------ -; ID Head Tail Diam Type Setting (Losscoef) -; Node Node -;------------------------------------------------------ - -[PUMPS] -;------------------------------------------------------------------ -;ID Head Tail Properties -; Node Node -;------------------------------------------------------------------ -9 9 10 HEAD 1 - -[VSD_PUMPS] -;------------------------------------------------------------------------------------- -;Pump Node Setpoint Setpoint SetpointType Speed Speed ControlType -;ID ID Value Curve 0/1(pressure/HGL) min max 0/1(downstream node, any node) -;------------------------------------------------------------------------------------- - -[EMITTERS] -;------------------------------------------------------ -; Node Flow Coeff. -; ID -;------------------------------------------------------ - -[DEMANDS] -;------------------------------------------------------------------ -; NodeID Demand Pattern -;------------------------------------------------------------------ -10 0.000000 ;BASE -11 9.463530 ;BASE -12 9.463530 ;BASE -13 6.309020 ;BASE -21 9.463530 ;BASE -22 12.618039 ;BASE -23 9.463530 ;BASE -31 6.309020 ;BASE -32 6.309020 ;BASE - -[PATTERNS] -;ID Multipliers -1 1.000000 -1 1.200000 -1 1.400000 -1 1.600000 -1 1.400000 -1 1.200000 -1 1.000000 -1 0.800000 -1 0.600000 -1 0.400000 -1 0.600000 -1 0.800000 - -[STATUS] -;ID Status/Setting - -[CURVES] -;ID X-Value Y-Value -1 94.635295 76.200000 - -[CONTROLS] -LINK 9 OPEN IF NODE 2 BELOW 33.528000 -LINK 9 CLOSED IF NODE 2 ABOVE 42.672000 - -[RULES] - -[MIXING] -;Tank Model -2 MIXED -9 MIXED - -[QUALITY] -;------------------------------------------------------------------ -;Nodes Initial -;ID quality -;------------------------------------------------------------------ -10 0.500000 -11 0.500000 -12 0.500000 -13 0.500000 -21 0.500000 -22 0.500000 -23 0.500000 -31 0.500000 -32 0.500000 -2 1.000000 -9 1.000000 - -[SOURCES] -;----------------------------------------------- -;NODEID SRCTYPE STRENGTH PATTERN -;----------------------------------------------- - -[REACTIONS] -GLOBAL BULK -0.500000 -GLOBAL WALL -1.000000 -GLOBAL NewBulk 0.000000 0.000000 -ORDER BULK 1.000000 -ORDER WALL 1 -ROUGHNESS CORRELATION 0.000000 - -[ENERGY] -GLOBAL PRICE 0 -GLOBAL EFFIC 75 -DEMAND CHARGE 0 - -[TIMES] -Duration 24:0:0 -Hydraulic Timestep 1:0:0 -Quality Timestep 0:5:0 -Pattern Timestep 2:0:0 -Pattern Start 0:0:0 -Report Timestep 1:0:0 -Report Start 0:0:0 -Start Date 2022:10:13 -Start ClockTime 0:00:00 -STATISTIC NONE - -[REPORT] -;------------------------------------------------------ -STATUS FULL -SUMMARY YES -MESSAGES YES -ENERGY YES -NODES NONE -LINKS NONE - -[OPTIONS] -UNITS LPS -DIFFUSIVITY 1.000000 -HEADLOSS H-W -SPECIFIC GRAVITY 1.000000 -VISCOSITY 1.000000 -TRIALS 40 -TOLERANCE 0.010000 -ACCURACY 0.001000 -Quality NONE -PATTERN 1 -EMITTER EXPONENT 0.500000 -CHECKFREQ 2 -MAXCHECK 10.000000 -DAMPLIMIT 0.000000 -DEMAND MULTIPLIER 1.000000 -UNBALANCED CONTINUE 10 - -[TURBINES] -;------------------------------------------------------ -; ID -;------------------------------------------------------ - -[COORDINATES] -;------------------------------------------------------ -;Node X-coord Y-coord -;ID -;------------------------------------------------------ -10 6.096000 21.336000 -11 9.144000 21.336000 -12 15.240000 21.336000 -13 21.336000 21.336000 -21 9.144000 12.192000 -22 15.240000 12.192000 -23 21.336000 12.192000 -31 9.144000 3.048000 -32 15.240000 3.048000 -2 15.240000 27.432000 -9 3.048000 21.336000 - -[VERTICES] -;------------------------------------------------------ -;Link X-coord Y-coord -;ID -;------------------------------------------------------ - -[END] diff --git a/tests/testdata/network_sensor_1.csv b/tests/testdata/network_sensor_1.csv index 904d9eb79..d235a45e6 100644 --- a/tests/testdata/network_sensor_1.csv +++ b/tests/testdata/network_sensor_1.csv @@ -1,111 +1,111 @@ ,water_level@sens1 -1994-08-07 16:35:06.721389014,193.7479319011718 -1994-08-07 16:36:11.808982110,193.9276622504125 -1994-08-07 16:36:58.463517098,193.73969537883863 -1994-08-07 16:38:50.136489724,193.5324026294447 -1994-08-07 16:39:54.184260240,193.75098664628783 -1994-08-07 16:41:02.301898383,193.9631823043365 -1994-08-07 16:41:48.047551850,193.88949067602914 -1994-08-07 16:43:03.765627271,193.73298338692013 -1994-08-07 16:43:59.271576674,193.518740505735 -1994-08-07 16:44:53.976575879,193.80052813920184 -1994-08-07 16:45:52.150380498,193.76709749688646 -1994-08-07 16:46:59.335689619,193.87266429057766 -1994-08-07 16:47:48.671800096,193.61027606890661 -1994-08-07 16:49:09.441634441,193.90646493021583 -1994-08-07 16:50:05.221428246,193.68537026321115 -1994-08-07 16:51:31.750429245,194.00527903620747 -1994-08-07 16:52:44.709396591,193.960610633433 -1994-08-07 16:54:15.295748944,193.87369664367992 -1994-08-07 16:56:09.081416788,193.74406275881358 -1994-08-07 16:57:14.043033644,193.71501017501876 -1994-08-07 16:58:18.210601321,193.83703675670696 -1994-08-07 16:59:03.257072206,194.06250124233108 -1994-08-07 17:00:06.081835904,193.97168058658363 -1994-08-07 17:01:06.426608705,193.6410669817052 -1994-08-07 17:02:22.837775497,193.7119913501634 -1994-08-07 17:03:18.768918912,193.8826429315821 -1994-08-07 17:04:17.424400499,193.76142364154504 -1994-08-07 17:05:06.371871595,193.82686832219773 -1994-08-07 17:06:17.991770538,193.7814248098547 -1994-08-07 17:07:14.600956935,193.747104244197 -1994-08-07 17:08:19.929202980,193.88962326399343 -1994-08-07 17:09:18.863933688,193.8894988894418 -1994-08-07 17:10:21.785300698,193.7678915506987 -1994-08-07 17:11:22.760047207,193.8350592978198 -1994-08-07 17:12:16.293904286,193.76937160755946 -1994-08-07 17:13:18.937531227,193.7072710276047 -1994-08-07 17:14:09.393470503,194.01678510826014 -1994-08-07 17:15:16.341863122,193.81790025709154 -1994-08-07 17:16:17.315241669,193.96796138622275 -1994-08-07 17:17:15.246401107,193.93776810871083 -1994-08-07 17:18:05.749523838,193.92373793354915 -1994-08-07 17:19:22.112742914,193.75041032904946 -1994-08-07 17:20:31.310425275,193.92249310948324 -1994-08-07 17:21:21.982712671,193.82335772625748 -1994-08-07 17:22:23.767907448,193.95466062588378 -1994-08-07 17:23:39.680152861,194.013618376674 -1994-08-07 17:24:38.147955485,194.11815372525726 -1994-08-07 17:25:38.420371252,194.50584562429492 -1994-08-07 17:26:24.408303070,194.54446646687336 -1994-08-07 17:27:31.105998416,194.46853129987517 -1994-08-07 17:28:34.880232331,194.55187457703428 -1994-08-07 17:29:37.175071072,194.86809034869842 -1994-08-07 17:30:35.271697419,194.70284194184958 -1994-08-07 17:31:31.204858260,194.98822903414495 -1994-08-07 17:32:24.847235710,195.24737706568624 -1994-08-07 17:33:36.679829931,195.2715904795984 -1994-08-07 17:34:37.932637257,195.1555004434547 -1994-08-07 17:35:25.487179907,195.11808508664626 -1994-08-07 17:36:22.047650391,195.35302938305526 -1994-08-07 17:37:22.665545301,195.058608262039 -1994-08-07 17:38:23.922713966,195.03890774364643 -1994-08-07 17:39:37.300808590,195.10925487796052 -1994-08-07 17:40:39.123378882,194.9197940042712 -1994-08-07 17:41:39.736323741,194.91541376013848 -1994-08-07 17:42:32.624612495,194.84419544155747 -1994-08-07 17:43:38.386604464,194.91238993528924 -1994-08-07 17:44:35.752954642,194.80146002502337 -1994-08-07 17:45:47.706770126,194.91559552756155 -1994-08-07 17:46:56.228029771,194.72765509600865 -1994-08-07 17:47:50.302639903,194.72477357208479 -1994-08-07 17:48:51.313688841,194.64138661647883 -1994-08-07 17:50:04.027931558,194.66788700646532 -1994-08-07 17:50:59.170111019,194.69887586125873 -1994-08-07 17:52:19.507804755,194.75091927194543 -1994-08-07 17:53:36.244289704,194.73563801241843 -1994-08-07 17:54:46.006725041,194.35800960937343 -1994-08-07 17:55:50.875697661,194.41711389439024 -1994-08-07 17:57:01.668197445,194.29313729304636 -1994-08-07 17:58:16.481604045,194.45199484204932 -1994-08-07 17:59:47.768779742,194.45872420226013 -1994-08-07 18:00:49.317493448,194.4314547834637 -1994-08-07 18:02:02.027423669,194.2515095971401 -1994-08-07 18:03:55.326990060,194.2601910363553 -1994-08-07 18:05:08.677524139,194.09049632407513 -1994-08-07 18:05:55.807125909,194.25764674606657 -1994-08-07 18:07:06.508171149,194.15893464151893 -1994-08-07 18:08:14.768585338,194.32278841101814 -1994-08-07 18:08:59.434113874,194.1892503649891 -1994-08-07 18:10:10.361033654,194.3360223971569 -1994-08-07 18:11:39.416513356,193.91053863482347 -1994-08-07 18:13:12.292137298,194.03847881603537 -1994-08-07 18:14:50.579821036,194.19255380323284 -1994-08-07 18:16:11.445107220,194.2372052362117 -1994-08-07 18:17:13.979038396,194.02910039993844 -1994-08-07 18:19:11.411173338,194.1543403568542 -1994-08-07 18:20:05.747471067,194.28718925009687 -1994-08-07 18:21:08.770390114,194.2290999628811 -1994-08-07 18:22:04.993112618,193.88244373099988 -1994-08-07 18:23:12.416131227,194.02896457246328 -1994-08-07 18:24:13.427829087,193.99948814393267 -1994-08-07 18:25:12.565706666,194.16456490763096 -1994-08-07 18:26:07.842114358,194.02641628418803 -1994-08-07 18:26:58.397587323,194.16132614507512 -1994-08-07 18:28:03.529864903,194.15134556849915 -1994-08-07 18:29:00.650668046,193.92727557471548 -1994-08-07 18:30:03.066222312,193.9766219050112 -1994-08-07 18:31:10.120818363,194.06760532977663 -1994-08-07 18:32:09.431674937,193.95571034292806 -1994-08-07 18:33:00.389496491,194.03147735060986 -1994-08-07 18:35:01.353919658,193.94705444753936 +1994-08-07 16:35:09.248945899,194.63116860768395 +1994-08-07 16:35:56.905645917,194.5676707622657 +1994-08-07 16:37:07.504970118,194.6462660461929 +1994-08-07 16:38:51.845566196,194.7338001780236 +1994-08-07 16:39:51.524809888,194.55808185491048 +1994-08-07 16:40:46.565738947,194.55808349668789 +1994-08-07 16:41:58.019286680,194.73941847393354 +1994-08-07 16:42:55.881580465,194.6582406652981 +1994-08-07 16:43:46.857575025,194.53454975378932 +1994-08-07 16:44:51.400929285,194.6357531967414 +1994-08-07 16:46:03.993317719,194.53517068189063 +1994-08-07 16:46:50.619237813,194.53493947581484 +1994-08-07 16:47:48.725897442,194.60572393711755 +1994-08-07 16:49:01.263055206,194.39024546186235 +1994-08-07 16:50:20.972009082,194.41036344135807 +1994-08-07 16:51:24.370105430,194.5348051826228 +1994-08-07 16:52:52.851710948,194.50676437331813 +1994-08-07 16:54:27.261392307,194.6440345965408 +1994-08-07 16:56:07.640750880,194.52948262662755 +1994-08-07 16:57:17.452326972,194.48080942478833 +1994-08-07 16:58:10.243662654,194.7691845057984 +1994-08-07 16:59:15.534116612,194.60013355159197 +1994-08-07 17:00:15.558594215,194.6293114142188 +1994-08-07 17:01:13.603493681,194.47985489329272 +1994-08-07 17:02:04.693795401,194.5675404873131 +1994-08-07 17:03:19.594049912,194.63264377264287 +1994-08-07 17:04:09.303601299,194.50593335710153 +1994-08-07 17:05:06.618370208,194.65799216511581 +1994-08-07 17:06:03.703502831,194.55968710757062 +1994-08-07 17:07:14.705858864,194.58986443849724 +1994-08-07 17:08:16.439287237,194.55811547158956 +1994-08-07 17:09:03.219756579,194.80276627059934 +1994-08-07 17:10:13.129861166,194.61544104901057 +1994-08-07 17:11:07.417915504,194.51028725671378 +1994-08-07 17:12:15.791455808,194.69759567773374 +1994-08-07 17:13:06.375328580,194.49257017601852 +1994-08-07 17:14:16.706754762,194.63486951379736 +1994-08-07 17:15:10.622706926,194.4174057903464 +1994-08-07 17:16:21.622599775,194.4799591050711 +1994-08-07 17:17:05.638418883,194.6319145171416 +1994-08-07 17:18:09.709327021,194.68554099393705 +1994-08-07 17:19:05.157470425,194.62832762401743 +1994-08-07 17:20:31.958872366,194.59896983972993 +1994-08-07 17:21:39.089787068,194.5830335513395 +1994-08-07 17:22:26.701832554,194.50438473943981 +1994-08-07 17:23:34.742680921,194.68283735650428 +1994-08-07 17:24:37.887444004,194.79669430161496 +1994-08-07 17:25:32.647016232,195.10026483492658 +1994-08-07 17:26:32.136011567,195.07896326938652 +1994-08-07 17:27:26.380045818,194.94288238583093 +1994-08-07 17:28:23.405055356,195.3331438705723 +1994-08-07 17:29:39.487315159,195.52371711375525 +1994-08-07 17:30:39.551361143,195.92504461637566 +1994-08-07 17:31:34.205029145,196.39768496286845 +1994-08-07 17:32:28.323595821,196.4704095225621 +1994-08-07 17:33:28.527191493,196.42975215741944 +1994-08-07 17:34:36.062113577,196.2166306158418 +1994-08-07 17:35:39.485205199,196.2221610378055 +1994-08-07 17:36:39.284728485,196.2296442874763 +1994-08-07 17:37:37.140510917,196.225179024431 +1994-08-07 17:38:34.383632923,195.97829717582485 +1994-08-07 17:39:23.225799300,195.80698036698206 +1994-08-07 17:40:24.775574282,195.50888859732595 +1994-08-07 17:41:39.514083771,195.31241669110756 +1994-08-07 17:42:33.671581193,195.34004164473942 +1994-08-07 17:43:24.726941032,195.22429282609926 +1994-08-07 17:44:31.973430857,194.98922843120135 +1994-08-07 17:45:51.207035382,195.0173454772892 +1994-08-07 17:46:38.038231677,194.8894198280907 +1994-08-07 17:47:44.153161028,194.72061375696137 +1994-08-07 17:48:53.350675787,194.7707739599649 +1994-08-07 17:49:58.685903954,194.86239130313098 +1994-08-07 17:51:10.575225190,194.68654190780777 +1994-08-07 17:52:08.897386189,194.83374097690952 +1994-08-07 17:53:36.876584427,194.4005163343176 +1994-08-07 17:54:35.873981750,194.7351138344219 +1994-08-07 17:55:49.506993963,194.65392260233162 +1994-08-07 17:57:04.285828102,194.61006813702372 +1994-08-07 17:58:23.194657981,194.64407903175513 +1994-08-07 18:00:03.239468210,194.43167523744617 +1994-08-07 18:00:59.521257846,194.6062676744975 +1994-08-07 18:02:12.069172067,194.66153523176055 +1994-08-07 18:03:57.196495357,194.7706073976382 +1994-08-07 18:05:02.677316061,194.56967871547732 +1994-08-07 18:06:00.627047354,194.53949670904663 +1994-08-07 18:07:00.202792868,194.5691022497431 +1994-08-07 18:08:14.783211095,194.70984160337176 +1994-08-07 18:09:03.184954493,194.6502914928019 +1994-08-07 18:10:13.163931104,194.56361638684987 +1994-08-07 18:11:46.360772520,194.66663741225665 +1994-08-07 18:13:24.414226071,194.62382823833323 +1994-08-07 18:14:54.276741862,194.7098100556939 +1994-08-07 18:16:07.135077693,194.54193153143257 +1994-08-07 18:17:15.986353876,194.57860768670741 +1994-08-07 18:19:01.871859756,194.57118652355396 +1994-08-07 18:20:12.416042305,194.46361859796022 +1994-08-07 18:21:03.582447249,194.6392159095424 +1994-08-07 18:21:58.453319329,194.63540423327268 +1994-08-07 18:23:10.876445918,194.60952013472675 +1994-08-07 18:24:01.509213588,194.58529067631093 +1994-08-07 18:25:16.776171687,194.46698343360745 +1994-08-07 18:26:17.045571540,194.5662423524891 +1994-08-07 18:27:16.264287804,194.57382181006608 +1994-08-07 18:28:05.370174005,194.52768242932783 +1994-08-07 18:28:58.276132331,194.59159847961465 +1994-08-07 18:30:16.533371252,194.647964289783 +1994-08-07 18:31:06.530682966,194.796025206332 +1994-08-07 18:32:17.300096381,194.6247118096035 +1994-08-07 18:33:17.239399542,194.63285647950195 +1994-08-07 18:35:07.060189109,194.59887865061089 diff --git a/tests/testdata/network_sensor_2.csv b/tests/testdata/network_sensor_2.csv index 9eddb84df..cb52da834 100644 --- a/tests/testdata/network_sensor_2.csv +++ b/tests/testdata/network_sensor_2.csv @@ -1,81 +1,81 @@ ,water_level@sens2 -1994-08-07 17:08:19.453000537,193.69907521870385 -1994-08-07 17:09:11.667777579,193.53254848297152 -1994-08-07 17:10:17.713878006,193.37840712805215 -1994-08-07 17:11:14.377409723,193.36046774853432 -1994-08-07 17:12:19.607715440,193.35883525251685 -1994-08-07 17:13:16.506158153,193.54074175140104 -1994-08-07 17:14:22.442398906,193.5636244747378 -1994-08-07 17:15:07.096815541,193.3936963704199 -1994-08-07 17:16:10.480296727,193.48972566299682 -1994-08-07 17:17:22.450643797,193.4587060771225 -1994-08-07 17:18:21.490266204,193.35182993861991 -1994-08-07 17:19:19.872035872,193.45517089047667 -1994-08-07 17:20:18.551963043,193.39190620106453 -1994-08-07 17:21:37.487970642,193.65287311723839 -1994-08-07 17:22:38.893023517,193.51519324042974 -1994-08-07 17:23:37.808072814,193.72342214376903 -1994-08-07 17:24:30.088355655,193.42926966972004 -1994-08-07 17:25:22.809359233,193.373787100516 -1994-08-07 17:26:33.203907398,193.87313878617707 -1994-08-07 17:27:24.450142559,193.77977426912494 -1994-08-07 17:28:34.256134409,193.87776964481588 -1994-08-07 17:29:29.162052421,193.74593301162298 -1994-08-07 17:30:40.417250997,194.06335624478052 -1994-08-07 17:31:36.292748137,194.0891460493891 -1994-08-07 17:32:22.577927686,193.93598437841732 -1994-08-07 17:33:35.944430514,193.89206968675506 -1994-08-07 17:34:23.631420564,194.29720825961996 -1994-08-07 17:35:33.310491457,193.94006446417492 -1994-08-07 17:36:36.768648209,193.94301852816398 -1994-08-07 17:37:41.256034582,193.93359649072713 -1994-08-07 17:38:27.439954370,193.99934207664205 -1994-08-07 17:39:32.423879950,194.0276140940857 -1994-08-07 17:40:32.505523785,193.75309351356177 -1994-08-07 17:41:26.182311995,193.86393199710076 -1994-08-07 17:42:31.811747815,193.86698395764947 -1994-08-07 17:43:43.977586264,193.79824583031655 -1994-08-07 17:44:32.820391407,193.88413939303942 -1994-08-07 17:45:39.780028021,193.82682735759107 -1994-08-07 17:46:45.520148467,193.64527654802706 -1994-08-07 17:47:47.632936535,193.79625317554138 -1994-08-07 17:48:57.346727826,193.6655550615617 -1994-08-07 17:49:49.253930336,193.78374770218835 -1994-08-07 17:51:02.833869005,193.49014557011174 -1994-08-07 17:52:08.948334997,193.5521603282343 -1994-08-07 17:53:32.956569310,193.42249475180054 -1994-08-07 17:54:40.776321387,193.41421417275032 -1994-08-07 17:55:45.154182165,193.7623513346016 -1994-08-07 17:57:01.877564139,193.71602664387882 -1994-08-07 17:58:27.973645801,193.6626855905494 -1994-08-07 17:59:48.164323924,193.58427431473757 -1994-08-07 18:00:49.608896563,193.81199176289348 -1994-08-07 18:02:06.114975563,193.64886741921137 -1994-08-07 18:04:10.676323539,193.719005472149 -1994-08-07 18:05:07.173323943,193.96465013641256 -1994-08-07 18:06:11.346838731,193.68426045579662 -1994-08-07 18:07:15.138616621,193.88023114770002 -1994-08-07 18:08:12.352725832,193.73742809783437 -1994-08-07 18:09:09.040990372,193.95542280957721 -1994-08-07 18:10:05.444299339,193.72699587134503 -1994-08-07 18:11:43.548122207,193.64415106941962 -1994-08-07 18:13:19.975340480,193.61679887917686 -1994-08-07 18:15:00.099149664,193.7092729650918 -1994-08-07 18:15:58.266937956,193.51481360263026 -1994-08-07 18:17:11.010370099,193.88621915401467 -1994-08-07 18:19:00.194728453,193.70300139682695 -1994-08-07 18:20:01.268579900,193.74549285164244 -1994-08-07 18:20:59.085548638,193.61744713398235 -1994-08-07 18:22:04.935328179,193.73830581406855 -1994-08-07 18:23:02.479745169,193.64939163691596 -1994-08-07 18:24:03.446628434,193.70495758379232 -1994-08-07 18:25:06.549919135,193.8685145360975 -1994-08-07 18:26:03.183612415,193.76014328305922 -1994-08-07 18:27:13.078601240,193.970374402854 -1994-08-07 18:28:04.443206888,193.861958294037 -1994-08-07 18:29:11.413727234,193.7661905820629 -1994-08-07 18:30:08.277252518,193.82534783818286 -1994-08-07 18:31:15.483217962,193.74254129620306 -1994-08-07 18:32:16.040549977,193.6307058726291 -1994-08-07 18:33:10.182896344,193.70636292657173 -1994-08-07 18:34:50.194959733,193.5921011184813 +1994-08-07 17:08:15.524744243,193.41774172138898 +1994-08-07 17:09:11.856910440,193.47503006530582 +1994-08-07 17:10:08.752215434,193.69875101291734 +1994-08-07 17:11:09.461290907,193.28090958913927 +1994-08-07 17:12:16.338369122,193.53653678563902 +1994-08-07 17:13:17.935490589,193.3068833201076 +1994-08-07 17:14:18.719580875,193.42114482611638 +1994-08-07 17:15:18.680362856,193.57718729602487 +1994-08-07 17:16:04.712122061,193.47439980366735 +1994-08-07 17:17:12.776406094,193.35960223119145 +1994-08-07 17:18:04.039175200,193.39503762223808 +1994-08-07 17:19:13.878577646,193.53353594676847 +1994-08-07 17:20:22.295610027,193.39014387882048 +1994-08-07 17:21:39.297083655,193.48084385700508 +1994-08-07 17:22:28.561300251,193.46238189102164 +1994-08-07 17:23:23.884340329,193.40931201113784 +1994-08-07 17:24:24.402833641,193.74250109838565 +1994-08-07 17:25:36.773212634,193.66380999305213 +1994-08-07 17:26:33.907361266,193.4666904532483 +1994-08-07 17:27:23.565453522,193.74230876155508 +1994-08-07 17:28:23.225136122,193.71708551367942 +1994-08-07 17:29:35.562382629,193.92884074558899 +1994-08-07 17:30:22.998260127,193.83009546033642 +1994-08-07 17:31:37.980201186,193.96308995448612 +1994-08-07 17:32:35.667844543,194.07190680895275 +1994-08-07 17:33:23.169975613,194.0927248114092 +1994-08-07 17:34:23.239754282,193.87580470988036 +1994-08-07 17:35:41.275791570,193.94614887543935 +1994-08-07 17:36:29.028415916,193.9220336671261 +1994-08-07 17:37:28.955842941,193.89400240389108 +1994-08-07 17:38:37.798991344,194.12552003340312 +1994-08-07 17:39:40.487971548,193.97825757051015 +1994-08-07 17:40:41.263021276,193.79784654108994 +1994-08-07 17:41:36.610563705,194.0001876839633 +1994-08-07 17:42:29.068191711,194.10310001911532 +1994-08-07 17:43:26.213014334,193.97411139421916 +1994-08-07 17:44:45.486938319,193.69665003953742 +1994-08-07 17:45:49.105084995,193.77434575775243 +1994-08-07 17:46:46.421440185,193.92446638347334 +1994-08-07 17:47:59.064087702,193.6988558561725 +1994-08-07 17:48:44.599949646,193.78237327582244 +1994-08-07 17:49:54.700502086,193.78045107624138 +1994-08-07 17:50:57.763072895,193.55986432588907 +1994-08-07 17:52:13.785212840,193.59158164408132 +1994-08-07 17:53:23.759065514,193.20106552673528 +1994-08-07 17:54:33.505358325,193.39316670461656 +1994-08-07 17:55:45.349524936,193.49595290165755 +1994-08-07 17:57:02.340206042,193.47277112028007 +1994-08-07 17:58:25.122897585,193.81485245851817 +1994-08-07 17:59:57.922375302,193.5351017069305 +1994-08-07 18:01:05.612450969,193.64782904742404 +1994-08-07 18:02:08.200411590,193.71931185069735 +1994-08-07 18:04:01.037241726,193.86397596171912 +1994-08-07 18:05:12.694982564,193.5800618561711 +1994-08-07 18:05:59.794916770,193.84255844064518 +1994-08-07 18:07:14.587450789,193.7292337553207 +1994-08-07 18:07:55.566089494,193.63173805091083 +1994-08-07 18:09:14.720576534,193.77780824781695 +1994-08-07 18:09:56.186198239,193.75338070100267 +1994-08-07 18:11:51.560862274,193.6769625652138 +1994-08-07 18:13:19.072022182,193.74736717138964 +1994-08-07 18:15:04.083295922,193.70481580828258 +1994-08-07 18:15:57.072931295,193.75642314565795 +1994-08-07 18:17:17.213085688,193.81256621686617 +1994-08-07 18:19:17.353050712,193.90645019235671 +1994-08-07 18:20:08.428956883,193.62476886515637 +1994-08-07 18:21:10.554972763,193.9625403980125 +1994-08-07 18:22:11.881973780,193.5547301848915 +1994-08-07 18:23:07.057821295,193.7354928772152 +1994-08-07 18:24:10.518161602,193.81023552924222 +1994-08-07 18:25:09.653286238,193.78025067603133 +1994-08-07 18:26:15.990160210,193.69062921793983 +1994-08-07 18:26:58.875927607,193.7328346255502 +1994-08-07 18:28:03.586263792,193.7050944377841 +1994-08-07 18:29:16.975229682,193.69619047743058 +1994-08-07 18:30:15.772275678,193.84080432591304 +1994-08-07 18:31:07.080135056,193.79218653394807 +1994-08-07 18:32:10.369651956,193.68786541254426 +1994-08-07 18:33:03.514623660,193.8476656760199 +1994-08-07 18:34:53.762423194,193.78941220306422 diff --git a/tests/testdata/network_sensor_3.csv b/tests/testdata/network_sensor_3.csv index 540162613..5e4d2435e 100644 --- a/tests/testdata/network_sensor_3.csv +++ b/tests/testdata/network_sensor_3.csv @@ -1,91 +1,91 @@ ,water_level@sens3 -1994-08-07 16:34:59.749185049,193.65573611633096 -1994-08-07 16:36:08.234735831,193.5492676961432 -1994-08-07 16:37:10.476205369,193.44270461528237 -1994-08-07 16:38:56.739070973,193.7316050738842 -1994-08-07 16:39:48.448422501,193.46705769407055 -1994-08-07 16:41:00.899011077,193.60328571871497 -1994-08-07 16:41:57.674627567,193.35125246300828 -1994-08-07 16:42:47.462417744,193.5185512837486 -1994-08-07 16:44:05.707731356,193.59592502393136 -1994-08-07 16:44:47.395900520,193.66394020062216 -1994-08-07 16:45:54.846537383,193.62178032405015 -1994-08-07 16:46:48.526040668,193.77009561152101 -1994-08-07 16:48:00.550698110,193.54903378032205 -1994-08-07 16:49:06.813081870,193.5248851167554 -1994-08-07 16:50:10.392793101,193.4807046599272 -1994-08-07 16:51:20.373948386,193.50658954793624 -1994-08-07 16:52:42.364153730,193.58489517430476 -1994-08-07 16:54:24.475220308,193.50643945831553 -1994-08-07 16:56:03.429894489,193.72466197230418 -1994-08-07 16:57:07.522109743,193.66315601778547 -1994-08-07 16:58:19.771112168,193.6526056046781 -1994-08-07 16:59:12.330382377,193.5774691241722 -1994-08-07 17:00:15.682992521,193.57194585146243 -1994-08-07 17:01:06.996793691,193.776140134919 -1994-08-07 17:02:03.506371148,193.68766157959166 -1994-08-07 17:03:10.614276582,193.49255884053937 -1994-08-07 17:04:12.713137359,193.62602129546661 -1994-08-07 17:05:11.105347604,193.6977653873034 -1994-08-07 17:06:08.694168494,193.76860864084824 -1994-08-07 17:07:10.279609860,193.50288362365427 -1994-08-07 17:08:21.003764660,193.55578257769696 -1994-08-07 17:09:06.412524613,193.56869342489048 -1994-08-07 17:10:15.705612895,193.40978399466348 -1994-08-07 17:11:09.451766070,193.60182994885278 -1994-08-07 17:12:08.055520793,193.51766966463958 -1994-08-07 17:13:10.657325255,193.7243146064135 -1994-08-07 17:14:21.633020060,193.51257532827768 -1994-08-07 17:15:09.075371450,193.55232084181247 -1994-08-07 17:16:17.914468302,193.62712317582472 -1994-08-07 17:17:16.975978261,193.5158279508316 -1994-08-07 17:18:08.275302169,193.52467436568807 -1994-08-07 17:19:15.119707735,193.8068739097595 -1994-08-07 17:20:17.654157988,193.58466106079132 -1994-08-07 17:21:35.874256489,193.6432198883477 -1994-08-07 17:22:26.201697028,193.7402389587467 -1994-08-07 17:23:40.614190845,193.8251455313248 -1994-08-07 17:24:35.163885201,194.0484457054399 -1994-08-07 17:25:33.831846465,194.2188581110414 -1994-08-07 17:26:29.463057842,194.14930206264734 -1994-08-07 17:27:32.301951282,194.26559908122894 -1994-08-07 17:48:51.253848509,194.47916510244187 -1994-08-07 17:49:47.468746981,194.53818636632545 -1994-08-07 17:51:17.048530468,194.42192673115431 -1994-08-07 17:52:06.196063531,194.3566111817392 -1994-08-07 17:53:33.571594531,194.49275563859712 -1994-08-07 17:54:40.295857745,194.24161289020807 -1994-08-07 17:55:51.426795930,194.32646211685966 -1994-08-07 17:57:07.010027886,194.3129625711656 -1994-08-07 17:58:14.788227728,194.21563049881922 -1994-08-07 17:59:55.817232777,193.97104689092097 -1994-08-07 18:01:00.899387468,194.01433322885484 -1994-08-07 18:02:11.092720572,194.06259026020842 -1994-08-07 18:04:08.113392108,194.0429608417371 -1994-08-07 18:05:13.679114222,193.96529261399613 -1994-08-07 18:05:59.122553354,194.15157054129583 -1994-08-07 18:06:57.704020361,194.05899018808853 -1994-08-07 18:08:00.962755607,193.87481824713652 -1994-08-07 18:09:12.803710813,193.94393360920478 -1994-08-07 18:09:55.783676414,193.90634365346295 -1994-08-07 18:11:38.994491765,194.00426585723756 -1994-08-07 18:13:23.817088485,194.21330958450045 -1994-08-07 18:15:02.660036684,193.89671511569932 -1994-08-07 18:16:07.012693812,193.78174880032458 -1994-08-07 18:17:20.710118446,193.86633562744598 -1994-08-07 18:19:15.130507240,194.0741744560302 -1994-08-07 18:20:15.800301822,194.00207588928566 -1994-08-07 18:21:05.594143504,193.98954078505082 -1994-08-07 18:22:10.672244133,193.96015892979668 -1994-08-07 18:22:59.116797345,193.8059099694073 -1994-08-07 18:24:17.782794557,193.83533534410043 -1994-08-07 18:25:04.518698881,193.9507667786435 -1994-08-07 18:26:01.010477471,193.74729023512035 -1994-08-07 18:26:58.886134140,194.11217785860217 -1994-08-07 18:28:09.564788837,193.8869974441292 -1994-08-07 18:29:08.171733686,193.8225247414004 -1994-08-07 18:30:08.862763988,193.94471797299423 -1994-08-07 18:30:58.204733811,193.73271910461622 -1994-08-07 18:32:15.465370257,193.6686396836888 -1994-08-07 18:33:11.959829548,193.89010296389915 -1994-08-07 18:35:04.487313817,193.81745709215483 +1994-08-07 16:34:51.738405763,195.71911372346744 +1994-08-07 16:36:02.612130836,195.87901934646473 +1994-08-07 16:37:09.296822360,195.85571210005557 +1994-08-07 16:39:00.736789484,195.9466952624367 +1994-08-07 16:39:54.461190925,195.9658314813668 +1994-08-07 16:40:48.379606056,196.11928822649972 +1994-08-07 16:41:51.503518116,196.02671352639672 +1994-08-07 16:42:53.089645928,195.8739807770119 +1994-08-07 16:43:58.746344827,195.84184493423234 +1994-08-07 16:44:57.243566093,195.98087831851782 +1994-08-07 16:45:52.949934518,195.7996630806651 +1994-08-07 16:47:05.558304976,196.11483227795262 +1994-08-07 16:47:57.943496387,196.04964567222837 +1994-08-07 16:48:56.218535835,195.8848146125239 +1994-08-07 16:50:03.294649452,195.7604492424034 +1994-08-07 16:51:22.586182784,196.06716519151811 +1994-08-07 16:52:44.328154568,195.9203239695763 +1994-08-07 16:54:15.242627465,196.0555595852989 +1994-08-07 16:56:06.619340481,195.77233518822212 +1994-08-07 16:57:08.589903374,195.87184045180618 +1994-08-07 16:58:06.355471906,195.9323023240734 +1994-08-07 16:59:20.823308493,195.93647601347803 +1994-08-07 17:00:04.492674913,195.88677140695364 +1994-08-07 17:01:13.378227791,195.99406294733632 +1994-08-07 17:02:11.095936540,195.8250159111633 +1994-08-07 17:03:22.535572338,195.91754000559942 +1994-08-07 17:04:05.128778043,195.94380751727275 +1994-08-07 17:05:10.845111981,195.98322183750744 +1994-08-07 17:06:22.277408666,196.00293944191046 +1994-08-07 17:07:20.198142518,195.81931374491776 +1994-08-07 17:08:19.229441419,195.778366537028 +1994-08-07 17:09:08.046056541,196.0595456362914 +1994-08-07 17:10:06.305751748,195.96500935529951 +1994-08-07 17:11:16.260864398,195.8569293004459 +1994-08-07 17:12:21.475519783,196.0868931516538 +1994-08-07 17:13:14.023257860,195.94334541753085 +1994-08-07 17:14:14.320253789,196.04970767250794 +1994-08-07 17:15:08.487581873,195.93852980224258 +1994-08-07 17:16:18.277858664,196.13785274658977 +1994-08-07 17:17:06.628874971,196.1073120383459 +1994-08-07 17:18:09.361584728,195.90688153925365 +1994-08-07 17:19:11.396728772,196.028935049197 +1994-08-07 17:20:23.617207574,195.99631554906009 +1994-08-07 17:21:26.391194648,196.0686411098548 +1994-08-07 17:22:23.839736495,195.83525509046544 +1994-08-07 17:23:33.755400849,196.00029154736703 +1994-08-07 17:24:27.315611065,196.03748307368497 +1994-08-07 17:25:33.167764428,195.76623420565457 +1994-08-07 17:26:24.630254305,195.92117915361624 +1994-08-07 17:27:31.165802037,195.98591137695055 +1994-08-07 17:49:01.142809136,195.85024941685083 +1994-08-07 17:49:48.472661323,195.99692259588772 +1994-08-07 17:50:58.865925347,195.89115758777214 +1994-08-07 17:52:19.234412986,195.86315656443003 +1994-08-07 17:53:34.122462264,195.93024545033268 +1994-08-07 17:54:47.965575535,195.83614251580494 +1994-08-07 17:55:45.794447533,195.88334051380122 +1994-08-07 17:57:05.261346237,195.81821575078018 +1994-08-07 17:58:14.234546401,196.13375889024476 +1994-08-07 17:59:49.528118857,195.94018712179874 +1994-08-07 18:00:49.654315959,195.86629148728667 +1994-08-07 18:02:16.994494405,195.95721952427655 +1994-08-07 18:04:08.626944414,195.92406992940593 +1994-08-07 18:05:05.784308495,195.91297689258573 +1994-08-07 18:06:02.499609682,195.99627689949654 +1994-08-07 18:07:12.867010816,196.0104126362391 +1994-08-07 18:08:03.171902148,195.88142864500458 +1994-08-07 18:09:11.654988789,195.87672908902147 +1994-08-07 18:10:04.105698171,195.90665315548014 +1994-08-07 18:11:41.276888588,195.7037068093077 +1994-08-07 18:13:17.771595713,195.7821661965145 +1994-08-07 18:14:50.251557483,196.07017436522102 +1994-08-07 18:16:10.549187604,196.09784638072512 +1994-08-07 18:17:16.190407802,195.9083086763178 +1994-08-07 18:19:02.611253903,195.9907153886884 +1994-08-07 18:20:15.958491465,195.96412367268093 +1994-08-07 18:21:05.644824427,196.24081044412677 +1994-08-07 18:22:08.838057222,196.04481881926847 +1994-08-07 18:23:16.096442219,195.92000853382066 +1994-08-07 18:24:10.451759918,195.83718521375246 +1994-08-07 18:25:00.304960814,195.77204884941955 +1994-08-07 18:26:16.763642472,195.9529788098758 +1994-08-07 18:27:10.521161061,195.85695159539344 +1994-08-07 18:28:04.665112293,195.7903155225949 +1994-08-07 18:29:00.752441453,195.86783782876324 +1994-08-07 18:30:13.847503785,195.82429454045888 +1994-08-07 18:31:10.368455119,196.10113298674943 +1994-08-07 18:32:08.636221840,196.02053702256995 +1994-08-07 18:33:15.844851661,195.93154526516523 +1994-08-07 18:35:05.771944224,196.08027590802962 diff --git a/tests/testdata/network_sensor_4.csv b/tests/testdata/network_sensor_4.csv new file mode 100644 index 000000000..fd0a4dc39 --- /dev/null +++ b/tests/testdata/network_sensor_4.csv @@ -0,0 +1,111 @@ +,discharge@sens4 +1994-08-07 16:34:50.260038470,-0.022968044977012207 +1994-08-07 16:35:53.580170617,-0.0424847304651167 +1994-08-07 16:37:01.717725103,0.041539004107529656 +1994-08-07 16:38:46.358644077,-0.04276111957632404 +1994-08-07 16:39:49.456708702,0.0036318757328853086 +1994-08-07 16:40:57.488831219,-0.02381780873893338 +1994-08-07 16:41:54.256491012,0.024027040622890135 +1994-08-07 16:43:03.681434222,0.01677525880364645 +1994-08-07 16:44:02.176871235,0.05198356057524611 +1994-08-07 16:44:52.664347034,-0.0253803624044502 +1994-08-07 16:45:51.016468669,-0.013360482813384684 +1994-08-07 16:46:53.421848163,-0.04879341863615496 +1994-08-07 16:47:57.633898850,-0.022057279849659207 +1994-08-07 16:48:56.835272816,0.019045448261223685 +1994-08-07 16:50:13.741978157,0.03809346522828643 +1994-08-07 16:51:27.717233044,-0.045668600841287287 +1994-08-07 16:52:50.449943617,0.044341641737401946 +1994-08-07 16:54:20.751530583,0.06920907109494705 +1994-08-07 16:56:08.777315191,0.022637940109366553 +1994-08-07 16:57:21.857066139,0.09606796731481608 +1994-08-07 16:58:18.160115883,-0.036213220378242886 +1994-08-07 16:59:05.690263515,-0.05949562119244504 +1994-08-07 17:00:20.257359518,-0.08594066577238096 +1994-08-07 17:01:12.636623965,0.07805990347653718 +1994-08-07 17:02:20.779044538,0.036224775641065236 +1994-08-07 17:03:18.885105119,0.000974066279370721 +1994-08-07 17:04:11.392270089,0.017979085142138335 +1994-08-07 17:05:03.337386166,-0.052090903396633505 +1994-08-07 17:06:08.261547188,0.1266789576804909 +1994-08-07 17:07:13.720684293,0.01105310803435355 +1994-08-07 17:08:15.557564397,0.010245784013306863 +1994-08-07 17:09:08.045753709,0.04123708366152381 +1994-08-07 17:10:05.675121481,0.029176808420839587 +1994-08-07 17:11:19.586604736,0.01652286530444095 +1994-08-07 17:12:22.576043614,-0.033951998856629056 +1994-08-07 17:13:13.401803645,0.029440164274977868 +1994-08-07 17:14:06.321585717,0.10031725865126515 +1994-08-07 17:15:08.334146530,0.07388540610692511 +1994-08-07 17:16:03.255813531,0.08670893146005428 +1994-08-07 17:17:21.173976131,-0.018055338546103545 +1994-08-07 17:18:05.243021658,-0.04151461668050107 +1994-08-07 17:19:14.418329509,0.0021270403162046113 +1994-08-07 17:20:18.946104415,0.011893618337954532 +1994-08-07 17:21:32.626560050,0.06486702230052678 +1994-08-07 17:22:34.571407767,-0.07090875592853291 +1994-08-07 17:23:38.137836074,0.10201067057984985 +1994-08-07 17:24:25.671425435,0.04404780970762432 +1994-08-07 17:25:21.762916573,0.06014277512783117 +1994-08-07 17:26:24.280712602,0.07569550359760258 +1994-08-07 17:27:39.543372837,0.11327422311027903 +1994-08-07 17:28:39.020801551,0.41897911039583435 +1994-08-07 17:29:33.491262043,0.5732617717152857 +1994-08-07 17:30:33.553337209,0.7435153941914141 +1994-08-07 17:31:34.843733491,0.988697003436309 +1994-08-07 17:32:25.050425572,1.1949088203028275 +1994-08-07 17:33:39.831238918,1.4047650535059857 +1994-08-07 17:34:29.918410498,1.4484106893135336 +1994-08-07 17:35:29.205770565,1.5154819667239776 +1994-08-07 17:36:31.921354106,1.6172635422150143 +1994-08-07 17:37:22.482319336,1.618719749933637 +1994-08-07 17:38:24.868667375,1.4837746330018473 +1994-08-07 17:39:36.303672329,1.590039739897071 +1994-08-07 17:40:23.198973359,1.5425081297899201 +1994-08-07 17:41:33.606042189,1.5412284271807954 +1994-08-07 17:42:26.449982194,1.5465732091051774 +1994-08-07 17:43:32.328912281,1.4533252264224563 +1994-08-07 17:44:35.717874735,1.3230358253675831 +1994-08-07 17:45:45.050454329,1.2214452499730728 +1994-08-07 17:46:52.317918104,1.3566551603819144 +1994-08-07 17:47:46.879434312,1.1773778213818158 +1994-08-07 17:48:53.704092806,1.120051070270391 +1994-08-07 17:49:54.369008044,1.0651761537865896 +1994-08-07 17:51:10.809423307,1.0217954838803727 +1994-08-07 17:52:23.148594786,0.9516992395737858 +1994-08-07 17:53:37.284441944,0.875879730580976 +1994-08-07 17:54:35.427807572,0.8276472968497236 +1994-08-07 17:55:43.622662701,0.7858041861977572 +1994-08-07 17:56:54.601280886,0.6674410856811352 +1994-08-07 17:58:22.103558614,0.5613299975673441 +1994-08-07 17:59:47.283516270,0.5245249358383302 +1994-08-07 18:00:56.296324944,0.4739913706429343 +1994-08-07 18:02:12.639856978,0.5284544087408105 +1994-08-07 18:04:02.007877816,0.265016576394772 +1994-08-07 18:05:10.741244075,0.4265559501057361 +1994-08-07 18:05:57.454965063,0.41224715241472215 +1994-08-07 18:06:56.825755635,0.22682679956733665 +1994-08-07 18:08:09.886775124,0.29537217680690786 +1994-08-07 18:09:05.232826324,0.27751480448234545 +1994-08-07 18:10:09.091047929,0.21061763845441656 +1994-08-07 18:11:42.434546772,0.22225779719317962 +1994-08-07 18:13:13.446040665,0.18623931454490772 +1994-08-07 18:15:00.606046353,0.3112485102407154 +1994-08-07 18:16:11.585317579,0.2579886421810623 +1994-08-07 18:17:20.029929417,0.263899831427984 +1994-08-07 18:19:03.409902745,0.21751816004401203 +1994-08-07 18:20:09.771613337,0.11512665319176305 +1994-08-07 18:21:05.186477939,0.13652224897219753 +1994-08-07 18:21:59.798641467,0.1793490878302634 +1994-08-07 18:23:16.313271509,0.08664690783802602 +1994-08-07 18:24:00.703372618,0.17682198783952913 +1994-08-07 18:25:16.971747076,0.12337352003101995 +1994-08-07 18:26:06.887115459,0.11104861006273618 +1994-08-07 18:27:01.669658577,0.15999056832165454 +1994-08-07 18:28:08.805018948,0.14151910405658652 +1994-08-07 18:29:15.425916718,0.09631302257585966 +1994-08-07 18:30:12.611497728,0.1676074460932002 +1994-08-07 18:31:14.098222957,0.05111568284981775 +1994-08-07 18:32:11.142667334,0.13173566675125067 +1994-08-07 18:33:11.812531290,0.12649040047766952 +1994-08-07 18:35:06.983913031,0.07428685583352534