diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 47f109be3..d0609d633 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -25,8 +25,8 @@ jobs: version: "1.8.27" - name: Install dependencies - run: uv sync --group dev --group docs - + run: uv sync --group dev --group docs --group networks + - name: Build documentation run: just docs diff --git a/.github/workflows/full_test.yml b/.github/workflows/full_test.yml index 6451014d2..d65d49fa6 100644 --- a/.github/workflows/full_test.yml +++ b/.github/workflows/full_test.yml @@ -30,10 +30,30 @@ jobs: enable-cache: true - name: Install dependencies - run: uv sync --group test --no-dev + run: uv sync --group test --group networks --no-dev - name: Type check run: just typecheck - name: Test run: just test + + build-no-networks: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: extractions/setup-just@v3 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + python-version: "3.10" + enable-cache: true + + - name: Install dependencies (without networks) + run: uv sync --group test --no-dev + + - name: Test + run: just test diff --git a/.github/workflows/notebooks_test.yml b/.github/workflows/notebooks_test.yml index dde9b8792..dd64a1df6 100644 --- a/.github/workflows/notebooks_test.yml +++ b/.github/workflows/notebooks_test.yml @@ -21,7 +21,7 @@ jobs: python-version: "3.14" enable-cache: true - name: Install dependencies - run: uv sync --group test --group notebooks --no-dev + run: uv sync --group test --group notebooks --group networks --no-dev - name: Test notebooks run: | uv run pytest tests/notebooks/ diff --git a/docs/_quarto.yml b/docs/_quarto.yml index e08d85d7d..b113355c0 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -48,6 +48,9 @@ website: - user-guide/selecting-data.qmd - user-guide/plotting.qmd - user-guide/statistics.qmd + - section: "Extensions" + contents: + - user-guide/network.qmd - title: "Examples" contents: @@ -78,6 +81,7 @@ website: - api/observation.qmd - api/PointObservation.qmd - api/TrackObservation.qmd + - api/NodeObservation.qmd - section: "Model Result" href: api/model.qmd contents: @@ -87,6 +91,7 @@ website: - api/DfsuModelResult.qmd - api/GridModelResult.qmd - api/DummyModelResult.qmd + - api/NetworkModelResult.qmd - section: "Matching" contents: - api/match.qmd @@ -164,6 +169,7 @@ quartodoc: - observation - PointObservation - TrackObservation + - NodeObservation - title: Model Result desc: "" contents: @@ -175,6 +181,7 @@ quartodoc: - DfsuModelResult - GridModelResult - DummyModelResult + - NetworkModelResult - title: Matching desc: Matching functions contents: diff --git a/docs/images/res1d_network_mapping.png b/docs/images/res1d_network_mapping.png new file mode 100644 index 000000000..2930e9914 Binary files /dev/null and b/docs/images/res1d_network_mapping.png differ diff --git a/docs/user-guide/network.qmd b/docs/user-guide/network.qmd new file mode 100644 index 000000000..a2ff74478 --- /dev/null +++ b/docs/user-guide/network.qmd @@ -0,0 +1,405 @@ +--- +title: Networks +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: + +```bash +uv pip install modelskill[networks] +``` + +or + +```bash +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, NetworkEdge, EdgeBreakPoint + + +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 ExampleEdge(NetworkEdge): + """Edge connecting two nodes with a given length.""" + + def __init__( + self, + edge_id: str, + start: NetworkNode, + end: NetworkNode, + length: float, + breakpoints: list | None = None, + ): + self._id = edge_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(EdgeBreakPoint): + def __init__(self, edge_id: str, distance: float, data: pd.DataFrame): + self._id = (edge_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) +edge1 = ExampleEdge("r1", node_s1, node_s2, length=500.0, breakpoints=[bp]) +edge2 = ExampleEdge("r2", node_s2, node_s3, length=300.0) +network = Network(edges=[edge1, edge2]) +``` + +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 edges carry the topology and reach length between them. Break points along an edge (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 format. + +Currently, the only supported format is `mikeio.Res1D`. + +### Res1D file + +The quickest way to get a `Network` is from the path to a MIKE 1D result file: + +```{python} +# | echo: false + +path_to_res1d = "../../tests/testdata/network.res1d" +path_to_sensor_data_1 = "../../tests/testdata/network_sensor_1.csv" +path_to_sensor_data_2 = "../../tests/testdata/network_sensor_2.csv" +``` + +```{python} +from modelskill.network import Network + +network = Network.from_res1d(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_res1d(res) +``` + +A `Res1D` 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) + + +## Inspecting the Network + +### Available quantities + +```{python} +network.quantities +``` + +### Underlying graph + +The network exposes a `networkx.Graph` so you can use any NetworkX algorithm or +plotting function directly: + +```{python} +# | echo: false + +plot_kwargs = { + "font_size": 6, + "node_size": 130, + "node_color": "white", + "edgecolors": "black", + "with_labels": True, +} +``` + +```{python} +import networkx as nx +import matplotlib.pyplot as plt + +fig, ax = plt.subplots(figsize=(10, 9), layout="tight") +nx.draw(network.graph, ax=ax, **plot_kwargs) +plt.show() +``` + +### Timeseries data + +```{python} +# Multi-index DataFrame: columns are (node, quantity) +network.to_dataframe().head() +``` + +```{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. + +```{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(edge="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} +# Edge lookup +ids = network.find(edge="58l1", distance="start") +print(ids) +ids = network.find(edge="58l1", distance=[51.456, 77.185]) +print(ids) +ids = network.find(edge="58l1", distance=["start", 77.185]) +print(ids) +``` + +## Skill assessment workflow + +### 1. Wrap the Network in a NetworkModelResult + +```{python} +import modelskill as ms +from modelskill.model.network import NetworkModelResult + +mr = NetworkModelResult(network, name="MyModel", item="WaterLevel") +mr +``` + +### 2. Create NodeObservations and compute skill + +`NodeObservation` accepts a file path directly; the observation name is taken from the filename. + + +```{python} +obs_1 = ms.NodeObservation(path_to_sensor_data_1, node=network.find(node="78")) +obs_2 = ms.NodeObservation(path_to_sensor_data_2, node=network.find(node="46")) + +cc = ms.match(obs=[obs_1, obs_2], mod=mr) +cc.skill() +``` + +## 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 `NetworkEdge`. + +`NetworkNode` requires three properties: `id`, `data`, and `boundary`. +`NetworkEdge` requires five: `id`, `start`, `end`, `length`, and `breakpoints`. + + +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, NetworkEdge, 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 ExampleEdge(NetworkEdge): + """Edge connecting two nodes with a given length.""" + + def __init__( + self, edge_id: str, start: NetworkNode, end: NetworkNode, length: float, + breakpoints: list | None = None, + ): + self._id = edge_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 `NetworkEdge` with no intermediate points can return an empty `breakpoints` list. +::: + + +```{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) + +edge1 = ExampleEdge("r1", node_s1, node_s2, length=500.0) +edge2 = ExampleEdge("r2", node_s2, node_s3, length=300.0) + +network = Network(edges=[edge1, edge2]) +network +``` + +### Adding break points along a reach + +Break points represent intermediate chainage locations on a reach (e.g. cross-sections). Subclass `EdgeBreakPoint` the same way — implement `id` (a `(edge_id, distance)` tuple) and `data`: + +```python +from modelskill.network import EdgeBreakPoint + + +class ExampleBreakPoint(EdgeBreakPoint): + def __init__(self, edge_id: str, distance: float, data: pd.DataFrame): + self._id = (edge_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) +edge1 = ExampleEdge("r1", node_s1, node_s2, length=500.0, breakpoints=[bp]) +edge2 = ExampleEdge("r2", node_s2, node_s3, length=300.0) +network = Network(edges=[edge1, edge2]) +``` + + +## See also + +* [API reference — NetworkModelResult](../api/NetworkModelResult.qmd) +* [API reference — NodeObservation](../api/NodeObservation.qmd) \ No newline at end of file diff --git a/notebooks/Collection_systems_network.ipynb b/notebooks/Collection_systems_network.ipynb new file mode 100644 index 000000000..1df12e66c --- /dev/null +++ b/notebooks/Collection_systems_network.ipynb @@ -0,0 +1,1617 @@ +{ + "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_res1d(\"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", + "Edges: 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_res1d(\"../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": [ + { + "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/++03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47gkl3bxb2Efhg0bZmHxhND7fG0GKqiNwICKj2bgumDQ4tZbb7UoC17neUoDM0IIIYQQQqRHjuOftO4VLo4N/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+fPnm6P90EMPubfeesvWvWzZMhP9tAbLmTOnGz16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EEKIq0WV7iQMlWwHY7GCg8wQzsePH7c+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": [ + "g0 = network.graph.copy()\n", + "lengths = nx.get_edge_attributes(g0, \"length\")\n", + "max_len = max(lengths.values()) if lengths else 1.0\n", + "nx.set_edge_attributes(\n", + " g0,\n", + " {e: v / max_len for e, v in lengths.items()},\n", + " \"norm_length\",\n", + ")\n", + "\n", + "widthmap = [2 if 'boundary' in g0.nodes[node] else 1 for node in g0.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 = g0.number_of_nodes()\n", + "k = 10 / np.sqrt(n) # increase multiplier (5, 10, ...) until nodes stop overlapping\n", + "\n", + "pos = nx.kamada_kawai_layout(g0, weight=\"norm_length\", scale=10)\n", + "nx.draw(g0, 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": "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": 6, + "id": "d9d23a8b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "252" + ] + }, + "execution_count": 6, + "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": 7, + "id": "30c88717", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "131" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network.find(edge=\"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": 8, + "id": "e25a4ba8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([241, 3, 40], [131, 133])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network.find(node=[\"92\", \"101\", \"113\"]), network.find(edge=[\"44l1\", \"45l1\"], distance=[44.841, 37.206])" + ] + }, + { + "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:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "cb1ae550", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "({'node': '98'},\n", + " {'edge': '45l1', 'distance': 37.20599457458005},\n", + " [{'node': '98'}, {'edge': '45l1', 'distance': 37.20599457458005}])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "network.recall(252), network.recall(133), network.recall([252, 133]) " + ] + }, + { + "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:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "edec2e5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + ": WaterLevel" + ] + }, + "execution_count": 10, + "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 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:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "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]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "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@sens280-0.0180020.1032490.1016680.0789010.8355470.0005250.687278
\n", + "
" + ], + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "water_level@sens2 80 -0.018002 0.103249 0.101668 0.078901 0.835547 \n", + "\n", + " si r2 \n", + "observation \n", + "water_level@sens2 0.000525 0.687278 " + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The name is taken from the name of the series\n", + "node_id = network.find(edge=\"117l1\", distance=48.7)\n", + "single_obs = ms.NodeObservation(sensor_2, node=node_id)\n", + "cmp = ms.match(single_obs, network_model)\n", + "cmp.skill()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "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_280-0.0180020.1032490.1016680.0789010.8355470.0005250.687278
\n", + "
" + ], + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "network_sensor_2 80 -0.018002 0.103249 0.101668 0.078901 0.835547 \n", + "\n", + " si r2 \n", + "observation \n", + "network_sensor_2 0.000525 0.687278 " + ] + }, + "execution_count": 13, + "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, node=node_id)\n", + "cmp = ms.match(single_obs, network_model)\n", + "cmp.skill()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "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 280-0.0180020.1032490.1016680.0789010.8355470.0005250.687278
\n", + "
" + ], + "text/plain": [ + " n bias rmse urmse mae cc si \\\n", + "observation \n", + "Sensor 2 80 -0.018002 0.103249 0.101668 0.078901 0.835547 0.000525 \n", + "\n", + " r2 \n", + "observation \n", + "Sensor 2 0.687278 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The name is passed\n", + "single_obs = ms.NodeObservation(path_to_sensor2, node=node_id, 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": 15, + "id": "923a1d93", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsEAAAK9CAYAAADFUbHOAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAmjFJREFUeJzt3Qd4VNXWxvE3gRBqAkiCIk0QRFRABfsVC0UErN9VUUDsBQXLVcF6bVfsWCgWBLEgihUVUbFgVxRErBQRUBQQCKGFkvmetc+d3CQkMJPMzJny/z3PGM5kcvaZMxHeWbPO3mmBQCAgAAAAIIWk+30AAAAAQKwRggEAAJByCMEAAABIOYRgAAAApBxCMAAAAFIOIRgAAAAphxAMAACAlEMIBgAAQMohBAMAACDlEIIBIAUcccQR7gYA8BCCAfjq+eefV1paml5++eVtvte+fXv3vffff3+b7zVt2lSHHHJIWGONHDlS48aNUzQsXLjQHes999wTlf0ni7Vr1+qmm27S3nvvrVq1ammnnXZShw4dNHjwYP3xxx9KRoWFhe737rjjjlOTJk3c87bnf9ttt2njxo1+Hx6QsgjBAHx12GGHua8ff/xxifvXrFmjOXPmqGrVqvrkk09KfG/x4sXuFvzZeAjB2LHNmzfr8MMP1913361//OMfuu+++3Tttddqv/3207PPPqtffvklKU/j+vXrddZZZ2n58uW68MILNXz4cB1wwAHuzUCPHj0UCAT8PkQgJVX1+wAApLZGjRppt9122yYEf/bZZy4c/POf/9zme8HtcENwNGzZssVV+rBjr7zyimbOnKlnnnlGp59+eonvWUV006ZNCXsa7XfAjr969erbfK9atWrujVzxTy7OO+88NW/e3AXhadOmqUuXLjE+YgBUggH4zsKshaMNGzYU3WehYa+99nKVss8//7xE0LTvWevBoYce6rbHjh2ro446Srm5ucrMzFTbtm01atSoEmNY4Pj+++/14Ycfup+1W/Ee2dWrV+uyyy5zH1fbPnbffXfdeeedJcYt3vJg1byWLVu6x/7www8hP9eCggIXfGz/9rM23tVXX+3uD7KPyo888shtftaOZdddd9X//d//lbjPjsXOlQWwhg0b6oILLtCqVatCPqaKjPvcc89p//33V506dZSVlaV99tlHDzzwwHb3P3/+fPc1+LoVZ8du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AQccoHr16qlBgwbu+b777rthvTEq6zZx4sQSj/3yyy918cUXa//993fnzR4DAPAQgCODxTJQKRbaLCwWt/fee6tZs2basGFDieBnIdhW3Iv3IHzDDTfo7rvv1nnnnedWBnz11Vd1+umnuyB22mmnbfdnr7vuOp177rkl7lu3bp0uvPBCdevWrcT9X331lf7xj3/orLPOcoFz5syZbtz33ntPH330kdLTY/se9eGHH1atWrVUtar310Lp5b7tPNx555064YQTdOaZZ7qljsePH6+uXbvqiSeecM8jFH369NGxxx5b4r6DDjqoxLb9rjz++OPuDUiLFi30yy+/VPr5AUAyIABHDiEYldKjRw917NixzO+Fu1pXPPj99981fPhwXXTRRRo5cqS7z0Jt586dddVVV+mf//znNuGwOAuEpT399NPu6xlnnFHi/o8//nibxzZv3lzXXHONq4SWDoYVYfsbMGBASG88TjrpJFfdLW/VtiOPPFKLFi1yjwmycN+hQwfdeOONIYdgq3737du3xH2bN28usW3n385DjRo1dMkllxCCAYAAHHG0QyAmPcEWxKwKbIp/DB60dOlS/fTTT9uEoeICgYALdccff3yZy1TbMtYXXHBBpY7bqp12DMX3Y8dpoWzJkiX67LPPwt7ns88+6yqsZR13aVZBN6tXr3ZfrZrepk0bd7M/B61cuVK77LKLDjnkELdMcCTY+bWlvu1rWfbaa68SAdhkZma6qq6dm/z8/JDHsur4pk2byv1+w4YNXQAGAHioAEceIRiVkpeXpxUrVpS4lcVCZbBK+tRTTxXdgoYOHep6Yq0SWx4Lo1ZBnDJliguBxU2ePNkFuOIVxtLHVd6toKCg6GesJcECqx1LcdYHG/x+OJYvX6533nnHtRDYfkuzlgI7hj/++ENvv/22brrpJtWpU6doPAuCTz75pObNm+daLYIGDhzozr29ydheZToce+yxhwu5Nr6dx7/++iukn/vzzz9dT7PdQmF9xbVr13afFFi7iT1vAED5CMDRQTtErFmVbf360B4brIqW8/F0RFhwqcRFR126dNnmvrIqiQcffLBat27tAmHpj8LD0b9/f91+++16/vnn3UfxxVsOrEp82GGHFd2Xk5MT0j7Hjh3rKtXBirRVIUtfiGVVV2NhNRx2wZcF3dKtEEEzZsxw5ybIztFLL73kLpYL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V5Cd2MaNGyvaqlSp4ttf0n6OHQ/j29h+jJ+qr7nfr7fhNU+t19yv1zuV/19L1dfc7+edaq959g4qwEFcGBfnBg4cmJJjx8P4fknV1zxVX+9UPu+85ql33lP1Nff7efOal412CKCMj83sXWReXp7vlUnEBq95auH1Tj285igLlWCglMzMTN10003uK1IDr3lq4fVOPbzmKAuVYAAAAKQcKsEAAABIOYRgAAAApBxCMAAAAFIOIRgAAAAphxCMpDJ9+nT17t3brRRjK/m98sorJb7/119/acCAAe77NWvW1DHHHKO5c+eWeMwFF1ygli1bqkaNGsrJydHxxx+vn376KeRjuPDCC93Yw4cPj9jzQvy93j/++KOOO+44N51erVq13KpIixYt4qVK0td87dq1uuSSS9wiSfZzbdu21ejRo6PyHBH517z4amI9evQocz9lPfbGG2/ULrvs4l7zLl26lLtfJCZCMJLKunXr1L59e40YMaLMv9BOOOEELViwQK+++qpmzpypZs2aub/Y7OeC9t9/f40dO9aFnKlTp7qf69atm7Zu3brD8V9++WV9/vnn7i9jJO/rPX/+fB122GFq06aNPvjgA82ePVs33HCDqlevHrXnCn9f8yuuuEJvvfWWnn76afdzl112mQvFr732Gi9NArzmQVacsAAcirvuuksPPvige7PzxRdfuDe73bt318aNGyPyvBAHAkCSsl/vl19+uWj7559/dvfNmTOn6L6tW7cGcnJyAo899li5+/n222/dz82bN2+74y1ZsiSw6667uv03a9YscP/990fomSDeXu9TTz010LdvX16YFHrN99prr8Att9xS4r799tsvcN1111X6eSA2r/nMmTPd39FLly7dZj+lFRYWBnbeeefA3XffXXTf6tWrA5mZmYEJEybwkiUJKsFIGQUFBe5r8Wpdenq6m0T9448/LvNnrJJgFaPddttNTZo0KXffhYWF6tevn6666irttddeUTh6xMvrba/1G2+8odatW7uqUG5urg488MAdfrSKxP5//JBDDnFV399//91VH99//3398ssvroKM+H/N169fr9NPP91Vk3feeecd7vfXX3/Vn3/+6SrKQdb6ZP+vf/bZZxF/HvAHIRgpwz66btq0qYYOHapVq1Zp06ZNuvPOO7VkyRItXbq0xGNHjhyp2rVru9uUKVP0zjvvqFq1auXu2/ZTtWpVDRo0KAbPBH6+3suWLXP9ocOGDXO9h2+//bZOPPFEnXTSSfrwww95cZL0//GHHnrI9QFbT7A9zl57C1SHH354DJ4ZKvuaX3755e6NjPV/h8ICsGnYsGGJ+207+D0kPkIwUkZGRoZeeuklV72pX7++u4DCqjl2kYRVDoo744wzXG+ZhRqr+J1yyinl9oF9/fXXeuCBBzRu3LiQe82QuK+3VYKN/WNq/7B26NBBQ4YMUa9evbhQKklf82AItn5/qwbb//P33nuvBg4cqHfffTcGzwyVec3tNXvvvfe4WBnb8rsfA4iW7fV8WW/XsmXL3J8POOCAwMUXX1zufgoKCgI1a9YMPPvss2V+33p/09LSAlWqVCm62djp6emuNxjJ9Xrb96tWrRq49dZbS9x/9dVXBw455JBKPQfE52u+fv36QEZGRuD1118vcf8555wT6N69Oy9bnL/mgwcPLvfv6M6dO5e5r/nz57vHWB9xcYcffnhg0KBBEX9e8AeVYKQk6+2yqZFsupsZM2Zs9yMy+3vXbsHes9KsF9hmB5g1a1bRzWaHsP5gu/IcyfV620fhNh3azz//XOJ+q0TZVelIvtd88+bN7la6mlylSpWiTwYQv6+5fVJT+u9oc//997t+8LJYj7j1Dk+bNq3ovjVr1rhZIg4++OAYPSNEW9WojwDEkPVqzps3r8TFDfYXnn1MZn1jL7zwgvtL0v783XffafDgwW56neDFLTbNzsSJE922Pc76yqz30+aIPPbYY0v0od1xxx2uF3SnnXZyt9If0dlfoHvssUcMn33q8eP1NvYG59RTT3X9oEceeaSbOmvy5MluujQk32uelZWlzp07u9fdHmdvdqyNYvz48brvvvt4yeP8Nbe/i8u6GM4eb2G3rNfcWttsGrzbbrtNrVq1co+zaRCtwGH7RpLwqQINRMX777/vPsIqfTvzzDPd9x944IFA48aN3UebTZs2DVx//fXuo9Cg33//PdCjR49Abm6ue4w99vTTTw/89NNPJcaxfY4dO7bc42CKtOR/vceMGRPYfffdA9WrVw+0b98+8Morr8ToWac2v15zm1ZrwIABgUaNGrnXfI899gjce++9biotxPdrHmpbRenX3F7bG264IdCwYUM3NdrRRx/tpmRD8kiz//gdxAEAAIBYoicYAAAAKYcQDAAAgJRDCAYAAEDKIQQDAAAg5RCCAQAAkHIIwQAAAEg5hGAAAACkHEIwAAAAUg4hGACiqHnz5ho+fHjSnGNbGtqWlF29erXfh+KOw25169Ytum/cuHFF99uytwBQHkIwAFTA4sWLdfbZZ6tRo0aqVq2amjVrpsGDB+vvv/9OmvN5xBFHbBMkDznkEC1dulTZ2dmKB2PHjtUvv/xStH3qqae64zv44IN9PS4A8Y8QDABhWrBggTp27Ki5c+dqwoQJmjdvnkaPHq1p06a58LVy5UrfzunWrVtVWFgYtf1b4N95551dpTUeWBU4Nze3aLtGjRru+Ow4AWB7CMEAEKaBAwe6kPX222+rc+fOatq0qXr06KF3331Xv//+u6677roSj8/Pz1efPn1Uq1Yt7brrrhoxYkTR9wKBgP7973+7fWRmZrrK8qBBg4q+X1BQoH/961/u5+znDzzwQNeSUPzjfwuCr732mtq2bev28fjjj6t69erbtCxYpfqoo45yf7aKtR2T7bdmzZraZ599XKAPGjBggD788EM98MADRe0FCxcuLLMd4sUXX9Ree+3lxrb2j3vvvbfEuHbff/7zH1c5r1Onjnuujz76KL93AHxFCAaAMFiVd+rUqbr44otd1bE4q0CeccYZmjhxogu3QXfffbfat2+vmTNnasiQIS6MvvPOO0UB8v7779cjjzziKsuvvPKKC6RBl1xyiT777DM999xzmj17tv75z3/qmGOOcY8NWr9+ve68804Xfr///nt3DBaMbd/FK8R2XPY9s3HjRu2///564403NGfOHJ1//vnq16+fvvzyS/d9C79W1T7vvPNce4HdmjRpss35+Prrr3XKKafotNNO03fffecC/Q033ODCeXEWjK16bufAzt1FF12kn3/+md89AP4JAABC9vnnn1u6Dbz88stlfv++++5z3//rr7/cdrNmzQLHHHNMiceceuqpgR49erg/33vvvYHWrVsHNm3atM2+fvvtt0CVKlUCv//+e4n7jz766MDQoUPdn8eOHevGmzVrVonHDB48OHDUUUcVbU+dOjWQmZkZWLVqVbnPrWfPnoErr7yyaLtz585uP8W9//77brzgfk4//fRA165dSzzmqquuCrRt27Zo285B3759i7YLCwsDubm5gVGjRgUqY3uvQ1nHDgDFUQkGgIoVEEJ+bOmLtGz7xx9/dH+2yu6GDRvUokULV3V9+eWXtWXLFvc9q6xaBbd169aqXbt20c3aFObPn1+0P2vNaNeuXYkxrOJrrQt//PGH237mmWfUs2fPopkUbL+33nqrqzrXr1/f7dcq3IsWLQrrPNjzOPTQQ0vcZ9tWqbYxgoofn7VTWNV82bJlYY0FAJFECAaAMOy+++4uxAVDbGl2f7169ZSTkxPS/qzFwNoCRo4c6dorrFXg8MMP1+bNm7V27VpVqVLFtRzMmjWr6GZjWLtCkP1c6QvVOnXqpJYtW7o2CgvZFq6DrRDBFg3bxzXXXKP333/f7bd79+7atGlTVH4fMjIySmzb8UbzAj4A2JGqO3wEAKDITjvtpK5du7rQevnll5foC/7zzz9dxbV///4lQunnn39e4gza9p577lm0bfvo3bu3u9lFd23atHFV4H333ddVU61i+o9//CPsV8FCrx1P48aNlZ6e7irBQZ988omOP/549e3b121bILWpxuziuuIV5uLV3LLY87B9FWfbVr22AA8A8YpKMACE6eGHH3azNljldPr06W7O4LfeesuFY5tt4fbbb98mFN51110uZNrMEC+88IK7OM7YBWRjxoxxF6fZ1GtPP/20C8U277AFSQuyFqpfeukl/frrr+7CtTvuuMNd0LYj9rPffPONO57/+7//c7M3BLVq1cpdnPfpp5+6yvIFF1ygv/76a5tZHb744gs3K8SKFSvKrNxeeeWVbmo4a62w5/fkk0+682MzWgBAPCMEA0CYLEDOmDHD9fHazAjWdmCzKxx55JFuJgfrsS0dFO3xVtm97bbbdN9997kAbaxH97HHHnN9tNY3a9OsTZ482VWcg4tBWAi2feyxxx464YQT9NVXX7lpxkJp3TjggAPcrBLFWyHM9ddfr/32288dhy2KYT26tu/iLMhaNdeqw9beUVa/sO3j+eefd20Xe++9t2688Ubdcsstboq1cNgxhPszAFAZaXZ1XKX2AABAJVnl++abbw4rCFvLifU6lw7vwVDdoUOHpFqyGkBkUQkGAPjK5ja2ZZit4h0uW/DDep6DrAfaZrr46KOPInyUAJINlWAAQEKy5aqNtWzstttuRavzBXubrdWkQYMGvh4jgPhFCAYAAEDKoR0CAAAAKYcQDAAAgJRDCAYAAEDKIQQDAAAg5RCCAQAAkHIIwQAAAEg5hGAAAACkHEIwAAAAlGr+H+1TsU0bbC8GAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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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": 16, + "id": "752261bf", + "metadata": {}, + "outputs": [], + "source": [ + "sensor_df = pd.concat(real_sensors, axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "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.0022180.1198600.1198390.0892050.9626720.0006170.926537
water_level@sens280-0.0180020.1032490.1016680.0789010.8355470.0005250.687278
water_level@sens3900.0093940.0909650.0904790.0741200.9506560.0004670.902123
\n", + "
" + ], + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "water_level@sens1 110 0.002218 0.119860 0.119839 0.089205 0.962672 \n", + "water_level@sens2 80 -0.018002 0.103249 0.101668 0.078901 0.835547 \n", + "water_level@sens3 90 0.009394 0.090965 0.090479 0.074120 0.950656 \n", + "\n", + " si r2 \n", + "observation \n", + "water_level@sens1 0.000617 0.926537 \n", + "water_level@sens2 0.000525 0.687278 \n", + "water_level@sens3 0.000467 0.902123 " + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "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.0022180.1198600.1198390.0892050.9626720.0006170.926537
network_sensor_280-0.0180020.1032490.1016680.0789010.8355470.0005250.687278
water_level@sens3900.0093940.0909650.0904790.0741200.9506560.0004670.902123
\n", + "
" + ], + "text/plain": [ + " n bias rmse urmse mae cc \\\n", + "observation \n", + "water_level@sens1 110 0.002218 0.119860 0.119839 0.089205 0.962672 \n", + "network_sensor_2 80 -0.018002 0.103249 0.101668 0.078901 0.835547 \n", + "water_level@sens3 90 0.009394 0.090965 0.090479 0.074120 0.950656 \n", + "\n", + " si r2 \n", + "observation \n", + "water_level@sens1 0.000617 0.926537 \n", + "network_sensor_2 0.000525 0.687278 \n", + "water_level@sens3 0.000467 0.902123 " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modelskill", + "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.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml index 514b19143..f59300834 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,11 +4,11 @@ build-backend = "uv_build" [tool.uv.build-backend] source-exclude = ["**/.mypy_cache**", "**/.mypy_cache/**"] -wheel-exclude = ["**/.mypy_cache**","**/.mypy_cache/**"] +wheel-exclude = ["**/.mypy_cache**", "**/.mypy_cache/**"] [project] name = "modelskill" -version = "1.4.dev0" +version = "1.4.0a1" dependencies = [ "numpy > 1.24.4", "pandas >= 1.4, < 3.0", # TODO remove upper limit @@ -17,7 +17,7 @@ dependencies = [ "xarray", "netCDF4", "scipy", - "jinja2", # used for skill.style + "jinja2", # used for skill.style ] authors = [ @@ -41,22 +41,13 @@ classifiers = [ "Topic :: Scientific/Engineering", ] +[project.optional-dependencies] +networks = ["mikeio1d", "networkx"] + [dependency-groups] -dev = [ - "pytest", - "plotly >= 4.5", - "ruff==0.6.2", - "netCDF4", - "dask", -] +dev = ["pytest", "plotly >= 4.5", "ruff==0.6.2", "netCDF4", "dask"] -docs = [ - "quartodoc==0.11.1", - "nbformat", - "nbconvert", - "ipykernel", - "griffe<2", -] +docs = ["quartodoc==0.11.1", "nbformat", "nbconvert", "ipykernel", "griffe<2"] test = [ "pytest", @@ -70,6 +61,8 @@ test = [ notebooks = ["nbformat", "nbconvert", "jupyter", "plotly", "shapely", "seaborn"] +networks = ["mikeio1d", "networkx"] + [project.urls] "Homepage" = "https://github.com/DHI/modelskill" "Bug Tracker" = "https://github.com/DHI/modelskill/issues" diff --git a/src/modelskill/__init__.py b/src/modelskill/__init__.py index 0b3ce52f2..a6fa90a5f 100644 --- a/src/modelskill/__init__.py +++ b/src/modelskill/__init__.py @@ -38,13 +38,10 @@ TrackModelResult, GridModelResult, DfsuModelResult, + NetworkModelResult, DummyModelResult, ) -from .obs import ( - observation, - PointObservation, - TrackObservation, -) +from .obs import observation, PointObservation, TrackObservation, NodeObservation from .matching import from_matched, match from .configuration import from_config from .settings import options, get_option, set_option, reset_option, load_style @@ -94,9 +91,11 @@ def load(filename: Union[str, Path]) -> Comparer | ComparerCollection: "GridModelResult", "DfsuModelResult", "DummyModelResult", + "NetworkModelResult", "observation", "PointObservation", "TrackObservation", + "NodeObservation", "TimeSeries", "match", "from_matched", diff --git a/src/modelskill/comparison/_collection.py b/src/modelskill/comparison/_collection.py index 884abe81c..7b56ad9f2 100644 --- a/src/modelskill/comparison/_collection.py +++ b/src/modelskill/comparison/_collection.py @@ -10,7 +10,6 @@ Iterator, List, Union, - Optional, Mapping, Iterable, overload, @@ -286,13 +285,13 @@ def merge( def sel( self, - model: Optional[IdxOrNameTypes] = None, - observation: Optional[IdxOrNameTypes] = None, - quantity: Optional[IdxOrNameTypes] = None, - start: Optional[TimeTypes] = None, - end: Optional[TimeTypes] = None, - time: Optional[TimeTypes] = None, - area: Optional[List[float]] = None, + model: IdxOrNameTypes | None = None, + observation: IdxOrNameTypes | None = None, + quantity: IdxOrNameTypes | None = None, + start: TimeTypes | None = None, + end: TimeTypes | None = None, + time: TimeTypes | None = None, + area: List[float] | None = None, **kwargs: Any, ) -> "ComparerCollection": """Select data based on model, time and/or area. @@ -572,7 +571,7 @@ def gridded_skill( binsize: float | None = None, by: str | Iterable[str] | None = None, metrics: Iterable[str] | Iterable[Callable] | str | Callable | None = None, - n_min: Optional[int] = None, + n_min: int | None = None, **kwargs: Any, ) -> SkillGrid: """Skill assessment of model(s) on a regular spatial grid. diff --git a/src/modelskill/comparison/_collection_plotter.py b/src/modelskill/comparison/_collection_plotter.py index 2122b8488..64297ad03 100644 --- a/src/modelskill/comparison/_collection_plotter.py +++ b/src/modelskill/comparison/_collection_plotter.py @@ -6,7 +6,6 @@ List, Literal, Mapping, - Optional, Sequence, Tuple, Union, @@ -60,19 +59,19 @@ def scatter( quantiles: int | Sequence[float] | None = None, fit_to_quantiles: bool = False, show_points: bool | int | float | None = None, - show_hist: Optional[bool] = None, - show_density: Optional[bool] = None, - norm: Optional[colors.Normalize] = None, + show_hist: bool | None = None, + show_density: bool | None = None, + norm: colors.Normalize | None = None, backend: Literal["matplotlib", "plotly"] = "matplotlib", figsize: Tuple[float, float] = (8, 8), - xlim: Optional[Tuple[float, float]] = None, - ylim: Optional[Tuple[float, float]] = None, + xlim: Tuple[float, float] | None = None, + ylim: Tuple[float, float] | None = None, reg_method: str | bool = "ols", - title: Optional[str] = None, - xlabel: Optional[str] = None, - ylabel: Optional[str] = None, - skill_table: Optional[Union[str, List[str], Mapping[str, str], bool]] = None, - ax: Optional[Axes] = None, + title: str | None = None, + xlabel: str | None = None, + ylabel: str | None = None, + skill_table: Union[str, List[str], Mapping[str, str], bool] | None = None, + ax: Axes | None = None, **kwargs, ) -> Axes | list[Axes]: """Scatter plot tailored for comparing model output with observations. @@ -190,17 +189,17 @@ def _scatter_one_model( quantiles: int | Sequence[float] | None, fit_to_quantiles: bool, show_points: bool | int | float | None, - show_hist: Optional[bool], - show_density: Optional[bool], + show_hist: bool | None, + show_density: bool | None, backend: Literal["matplotlib", "plotly"], figsize: Tuple[float, float], - xlim: Optional[Tuple[float, float]], - ylim: Optional[Tuple[float, float]], + xlim: Tuple[float, float] | None, + ylim: Tuple[float, float] | None, reg_method: str | bool, - title: Optional[str], - xlabel: Optional[str], - ylabel: Optional[str], - skill_table: Optional[Union[str, List[str], Mapping[str, str], bool]], + title: str | None, + xlabel: str | None, + ylabel: str | None, + skill_table: Union[str, List[str], Mapping[str, str], bool] | None, ax, **kwargs, ): @@ -344,11 +343,11 @@ def hist( bins: int | Sequence = 100, *, model: str | int | None = None, - title: Optional[str] = None, + title: str | None = None, density: bool = True, alpha: float = 0.5, ax=None, - figsize: Optional[Tuple[float, float]] = None, + figsize: Tuple[float, float] | None = None, **kwargs, ): """Plot histogram of specific model and all observations. @@ -409,11 +408,11 @@ def _hist_one_model( *, mod_name: str, bins: int | Sequence, - title: Optional[str], + title: str | None, density: bool, alpha: float, ax, - figsize: Optional[Tuple[float, float]], + figsize: Tuple[float, float] | None, **kwargs, ): from ._comparison import MOD_COLORS @@ -464,7 +463,7 @@ def taylor( marker: str = "o", marker_size: float = 6.0, title: str = "Taylor diagram", - ) -> Optional[Figure]: + ) -> Figure | None: """Taylor diagram for model skill comparison. Taylor diagram showing model std and correlation to observation @@ -783,8 +782,8 @@ def _residual_hist_one_model( def spatial_overview( self, ax=None, - figsize: Optional[Tuple] = None, - title: Optional[str] = None, + figsize: Tuple | None = None, + title: str | None = None, ) -> Axes: """Plot observation points on a map showing the model domain diff --git a/src/modelskill/comparison/_comparer_plotter.py b/src/modelskill/comparison/_comparer_plotter.py index 0f548dfc8..820814235 100644 --- a/src/modelskill/comparison/_comparer_plotter.py +++ b/src/modelskill/comparison/_comparer_plotter.py @@ -3,7 +3,6 @@ Literal, Union, List, - Optional, Tuple, Sequence, TYPE_CHECKING, @@ -464,19 +463,19 @@ def scatter( quantiles: int | Sequence[float] | None = None, fit_to_quantiles: bool = False, show_points: bool | int | float | None = None, - show_hist: Optional[bool] = None, - show_density: Optional[bool] = None, - norm: Optional[colors.Normalize] = None, + show_hist: bool | None = None, + show_density: bool | None = None, + norm: colors.Normalize | None = None, backend: Literal["matplotlib", "plotly"] = "matplotlib", figsize: Tuple[float, float] = (8, 8), - xlim: Optional[Tuple[float, float]] = None, - ylim: Optional[Tuple[float, float]] = None, + xlim: Tuple[float, float] | None = None, + ylim: Tuple[float, float] | None = None, reg_method: str | bool = "ols", - title: Optional[str] = None, - xlabel: Optional[str] = None, - ylabel: Optional[str] = None, - skill_table: Optional[Union[str, List[str], Mapping[str, str], bool]] = None, - ax: Optional[matplotlib.axes.Axes] = None, + title: str | None = None, + xlabel: str | None = None, + ylabel: str | None = None, + skill_table: Union[str, List[str], Mapping[str, str], bool] | None = None, + ax: matplotlib.axes.Axes | None = None, **kwargs, ) -> matplotlib.axes.Axes | list[matplotlib.axes.Axes]: """Scatter plot tailored for model-observation comparison. @@ -594,18 +593,18 @@ def _scatter_one_model( quantiles: int | Sequence[float] | None, fit_to_quantiles: bool, show_points: bool | int | float | None, - show_hist: Optional[bool], - show_density: Optional[bool], - norm: Optional[colors.Normalize], + show_hist: bool | None, + show_density: bool | None, + norm: colors.Normalize | None, backend: Literal["matplotlib", "plotly"], figsize: Tuple[float, float], - xlim: Optional[Tuple[float, float]], - ylim: Optional[Tuple[float, float]], + xlim: Tuple[float, float] | None, + ylim: Tuple[float, float] | None, reg_method: str | bool, - title: Optional[str], - xlabel: Optional[str], - ylabel: Optional[str], - skill_table: Optional[Union[str, List[str], Mapping[str, str], bool]], + title: str | None, + xlabel: str | None, + ylabel: str | None, + skill_table: Union[str, List[str], Mapping[str, str], bool] | None, **kwargs, ): """Scatter plot for one model only""" diff --git a/src/modelskill/comparison/_comparison.py b/src/modelskill/comparison/_comparison.py index f8fedef75..2e68491dc 100644 --- a/src/modelskill/comparison/_comparison.py +++ b/src/modelskill/comparison/_comparison.py @@ -8,7 +8,6 @@ List, Literal, Mapping, - Optional, Union, Iterable, Sequence, @@ -20,11 +19,13 @@ import xarray as xr from copy import deepcopy +from ..model.network import NodeModelResult + from .. import metrics as mtr from .. import Quantity from ..types import GeometryType -from ..obs import PointObservation, TrackObservation +from ..obs import PointObservation, TrackObservation, NodeObservation from ..model import PointModelResult, TrackModelResult from ..timeseries._timeseries import _validate_data_var_name from ._comparer_plotter import ComparerPlotter @@ -49,6 +50,12 @@ Serializable = Union[str, int, float] +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"] + return data.drop_vars(coords_to_drop, errors="ignore") + + def _parse_dataset(data: xr.Dataset) -> xr.Dataset: if not isinstance(data, xr.Dataset): raise ValueError("matched_data must be an xarray.Dataset") @@ -60,12 +67,15 @@ def _parse_dataset(data: xr.Dataset) -> xr.Dataset: raise ValueError("Observation data must not contain missing values.") # coordinates - if "x" not in data.coords: - data.coords["x"] = np.nan - if "y" not in data.coords: - data.coords["y"] = np.nan - if "z" not in data.coords: - data.coords["z"] = np.nan + # 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: + if "x" not in data.coords: + data.coords["x"] = np.nan + if "y" not in data.coords: + data.coords["y"] = np.nan + if "z" not in data.coords: + data.coords["z"] = np.nan # Validate data vars = [v for v in data.data_vars] @@ -97,7 +107,11 @@ def _parse_dataset(data: xr.Dataset) -> xr.Dataset: # Validate attrs if "gtype" not in data.attrs: - data.attrs["gtype"] = str(GeometryType.POINT) + # Determine gtype based on available coordinates + if "node" in data.coords: + data.attrs["gtype"] = str(GeometryType.NODE) + else: + data.attrs["gtype"] = str(GeometryType.POINT) # assert "gtype" in data.attrs, "data must have a gtype attribute" # assert data.attrs["gtype"] in [ # str(GeometryType.POINT), @@ -173,8 +187,8 @@ class ItemSelection: obs: str model: Sequence[str] aux: Sequence[str] - x: Optional[str] = None - y: Optional[str] = None + x: str | None = None + y: str | None = None def __post_init__(self) -> None: # check that obs, model and aux are unique, and that they are not overlapping @@ -195,8 +209,8 @@ def all(self) -> Sequence[str]: def parse( items: Sequence[str], obs_item: str | int | None = None, - mod_items: Optional[Iterable[str | int]] = None, - aux_items: Optional[Iterable[str | int]] = None, + mod_items: Iterable[str | int] | None = None, + aux_items: Iterable[str | int] | None = None, x_item: str | int | None = None, y_item: str | int | None = None, ) -> ItemSelection: @@ -294,15 +308,15 @@ def _inside_polygon(polygon: Any, xy: np.ndarray) -> np.ndarray: def _matched_data_to_xarray( df: pd.DataFrame, obs_item: int | str | None = None, - mod_items: Optional[Iterable[str | int]] = None, - aux_items: Optional[Iterable[str | int]] = None, - name: Optional[str] = None, - x: Optional[float] = None, - y: Optional[float] = None, - z: Optional[float] = None, + mod_items: Iterable[str | int] | None = None, + aux_items: Iterable[str | int] | None = None, + name: str | None = None, + x: float | None = None, + y: float | None = None, + z: float | None = None, x_item: str | int | None = None, y_item: str | int | None = None, - quantity: Optional[Quantity] = None, + quantity: Quantity | None = None, ) -> xr.Dataset: """Convert matched data to accepted xarray.Dataset format""" assert isinstance(df, pd.DataFrame) @@ -444,7 +458,11 @@ class Comparer: def __init__( self, matched_data: xr.Dataset, - raw_mod_data: dict[str, PointModelResult | TrackModelResult] | None = None, + raw_mod_data: dict[ + str, + PointModelResult | TrackModelResult | NodeModelResult, + ] + | None = None, ) -> None: self.data = _parse_dataset(matched_data) self.raw_mod_data = ( @@ -464,18 +482,22 @@ def __init__( @staticmethod def from_matched_data( data: xr.Dataset | pd.DataFrame, - raw_mod_data: Optional[Dict[str, PointModelResult | TrackModelResult]] = None, + raw_mod_data: Dict[ + str, + PointModelResult | TrackModelResult | NodeModelResult, + ] + | None = None, obs_item: str | int | None = None, - mod_items: Optional[Iterable[str | int]] = None, - aux_items: Optional[Iterable[str | int]] = None, - name: Optional[str] = None, + mod_items: Iterable[str | int] | None = None, + aux_items: Iterable[str | int] | None = None, + name: str | None = None, weight: float = 1.0, - x: Optional[float] = None, - y: Optional[float] = None, - z: Optional[float] = None, + x: float | None = None, + y: float | None = None, + z: float | None = None, x_item: str | int | None = None, y_item: str | int | None = None, - quantity: Optional[Quantity] = None, + quantity: Quantity | None = None, ) -> "Comparer": """Initialize from compared data""" if not isinstance(data, xr.Dataset): @@ -582,7 +604,15 @@ def z(self) -> Any: """z-coordinate""" return self._coordinate_values("z") - def _coordinate_values(self, coord: str) -> Any: + @property + def node(self) -> Any: + """node-coordinate""" + return self._coordinate_values("node") + + def _coordinate_values(self, coord: str) -> None | 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 @@ -707,10 +737,10 @@ def rename( return Comparer(matched_data=data, raw_mod_data=raw_mod_data) - def _to_observation(self) -> PointObservation | TrackObservation: + def _to_observation(self) -> PointObservation | TrackObservation | NodeObservation: """Convert to Observation""" if self.gtype == "point": - df = self.data.drop_vars(["x", "y", "z"])[self._obs_str].to_dataframe() + df = _drop_scalar_coords(self.data)[self._obs_str].to_dataframe() return PointObservation( data=df, name=self.name, @@ -721,7 +751,9 @@ def _to_observation(self) -> PointObservation | TrackObservation: # TODO: add attrs ) elif self.gtype == "track": - df = self.data.drop_vars(["z"])[[self._obs_str]].to_dataframe() + df = self.data.drop_vars(["z"], errors="ignore")[ + [self._obs_str] + ].to_dataframe() return TrackObservation( data=df, item=0, @@ -731,10 +763,21 @@ def _to_observation(self) -> PointObservation | TrackObservation: quantity=self.quantity, # TODO: add attrs ) + elif self.gtype == "node": + df = _drop_scalar_coords(self.data)[self._obs_str].to_dataframe() + return NodeObservation( + data=df, + name=self.name, + node=self.node, + quantity=self.quantity, + # TODO: add attrs + ) else: raise NotImplementedError(f"Unknown gtype: {self.gtype}") - def _to_model(self) -> list[PointModelResult | TrackModelResult]: + def _to_model( + self, + ) -> list[PointModelResult | TrackModelResult | NodeModelResult]: mods = list(self.raw_mod_data.values()) return mods @@ -776,11 +819,11 @@ def merge( def sel( self, - model: Optional[IdxOrNameTypes] = None, - start: Optional[TimeTypes] = None, - end: Optional[TimeTypes] = None, - time: Optional[TimeTypes] = None, - area: Optional[List[float]] = None, + model: IdxOrNameTypes | None = None, + start: TimeTypes | None = None, + end: TimeTypes | None = None, + time: TimeTypes | None = None, + area: List[float] | None = None, ) -> "Comparer": """Select data based on model, time and/or area. @@ -896,9 +939,9 @@ def _to_long_dataframe( ) -> pd.DataFrame: """Return a copy of the data as a long-format pandas DataFrame (for groupby operations)""" - data = self.data.drop_vars("z", errors="ignore") + data = self.data.drop_vars(["z", "node"], errors="ignore") - # this step is necessary since we keep arbitrary derived data in the dataset, but not z + # 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 id_vars = [v for v in data.variables if v not in self.mod_names] @@ -981,8 +1024,8 @@ def skill( df = cmp._to_long_dataframe() res = _groupby_df(df, by=by, metrics=metrics) - res["x"] = np.nan if self.gtype == "track" else cmp.x - res["y"] = np.nan if self.gtype == "track" else cmp.y + res["x"] = np.nan if self.gtype == "track" or cmp.x is None else cmp.x + res["y"] = np.nan if self.gtype == "track" or cmp.y is None else cmp.y res = self._add_as_col_if_not_in_index(df, skilldf=res) return SkillTable(res) @@ -1138,7 +1181,7 @@ def gridded_skill( @property def _residual(self) -> np.ndarray: - df = self.data.drop_vars(["x", "y", "z"]).to_dataframe() + df = _drop_scalar_coords(self.data).to_dataframe() obs = df[self._obs_str].values mod = df[self.mod_names].values return mod - np.vstack(obs) @@ -1202,12 +1245,17 @@ def to_dataframe(self) -> pd.DataFrame: if self.gtype == str(GeometryType.POINT): # we remove the scalar coordinate variables as they # will otherwise be columns in the dataframe - return self.data.drop_vars(["x", "y", "z"]).to_dataframe() + return _drop_scalar_coords(self.data).to_dataframe() elif self.gtype == str(GeometryType.TRACK): - df = self.data.drop_vars(["z"]).to_dataframe() - # make sure that x, y cols are first - cols = ["x", "y"] + [c for c in df.columns if c not in ["x", "y"]] + df = self.data.drop_vars(["z"], errors="ignore").to_dataframe() + # make sure that x, y cols are first if they exist + coord_cols = [c for c in ["x", "y"] if c in df.columns] + other_cols = [c for c in df.columns if c not in ["x", "y"]] + cols = coord_cols + other_cols return df[cols] + elif self.gtype == str(GeometryType.NODE): + # For network data, drop node coordinate like other geometries drop their coordinates + return _drop_scalar_coords(self.data).to_dataframe() else: raise NotImplementedError(f"Unknown gtype: {self.gtype}") @@ -1258,7 +1306,10 @@ def load(filename: Union[str, Path]) -> "Comparer": return Comparer(matched_data=data) if data.gtype == "point": - raw_mod_data: Dict[str, PointModelResult | TrackModelResult] = {} + raw_mod_data: Dict[ + str, + PointModelResult | TrackModelResult | NodeModelResult, + ] = {} for var in data.data_vars: var_name = str(var) diff --git a/src/modelskill/comparison/_utils.py b/src/modelskill/comparison/_utils.py index f8d399d1a..2df4fa8d1 100644 --- a/src/modelskill/comparison/_utils.py +++ b/src/modelskill/comparison/_utils.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Callable, Optional, Iterable, List, Tuple, Union +from typing import Callable, Iterable, List, Tuple, Union from datetime import datetime import numpy as np import pandas as pd @@ -10,7 +10,7 @@ def _add_spatial_grid_to_df( - df: pd.DataFrame, bins, binsize: Optional[float] + df: pd.DataFrame, bins, binsize: float | None ) -> pd.DataFrame: if binsize is None: # bins from bins @@ -58,7 +58,7 @@ def _groupby_df( *, by: List[str | pd.Grouper], metrics: List[Callable], - n_min: Optional[int] = None, + n_min: int | None = None, ) -> pd.DataFrame: def calc_metrics(group: pd.DataFrame) -> pd.Series: # set index to time column (in most cases a DatetimeIndex, but not always) diff --git a/src/modelskill/matching.py b/src/modelskill/matching.py index b182f0ad7..f2fce0341 100644 --- a/src/modelskill/matching.py +++ b/src/modelskill/matching.py @@ -7,7 +7,6 @@ Iterable, Literal, Mapping, - Optional, Sequence, TypeVar, Union, @@ -27,21 +26,33 @@ from .model.dfsu import DfsuModelResult from .model.dummy import DummyModelResult from .model.grid import GridModelResult +from .model.network import NetworkModelResult, NodeModelResult from .model.track import TrackModelResult -from .obs import Observation, PointObservation, TrackObservation +from .model.point import align_data +from .obs import ( + Observation, + PointObservation, + TrackObservation, + NodeObservation, +) from .timeseries import TimeSeries from .types import Period TimeDeltaTypes = Union[float, int, np.timedelta64, pd.Timedelta, timedelta] -IdxOrNameTypes = Optional[Union[int, str]] -GeometryTypes = Optional[Literal["point", "track", "unstructured", "grid"]] +IdxOrNameTypes = Union[int, str] | None +GeometryTypes = Literal["point", "track", "unstructured", "grid"] | None MRTypes = Union[ PointModelResult, GridModelResult, DfsuModelResult, TrackModelResult, + NetworkModelResult, DummyModelResult, ] +FieldTypes = Union[ + GridModelResult, + DfsuModelResult, +] MRInputType = Union[ str, Path, @@ -56,7 +67,11 @@ TimeSeries, MRTypes, ] -ObsTypes = Union[PointObservation, TrackObservation] +ObsTypes = Union[ + PointObservation, + TrackObservation, + NodeObservation, +] ObsInputType = Union[ str, Path, @@ -67,7 +82,6 @@ pd.Series, ObsTypes, ] - T = TypeVar("T", bound="TimeSeries") @@ -75,14 +89,14 @@ def from_matched( data: Union[str, Path, pd.DataFrame, mikeio.Dfs0, mikeio.Dataset], *, obs_item: str | int | None = 0, - mod_items: Optional[Iterable[str | int]] = None, - aux_items: Optional[Iterable[str | int]] = None, - quantity: Optional[Quantity] = None, - name: Optional[str] = None, + mod_items: Iterable[str | int] | None = None, + aux_items: Iterable[str | int] | None = None, + quantity: Quantity | None = None, + name: str | None = None, weight: float = 1.0, - x: Optional[float] = None, - y: Optional[float] = None, - z: Optional[float] = None, + x: float | None = None, + y: float | None = None, + z: float | None = None, x_item: str | int | None = None, y_item: str | int | None = None, ) -> Comparer: @@ -176,8 +190,8 @@ def match( obs: ObsTypes, mod: MRTypes | Sequence[MRTypes], *, - max_model_gap: Optional[float] = None, - spatial_method: Optional[str] = None, + max_model_gap: float | None = None, + spatial_method: str | None = None, spatial_tolerance: float = 1e-3, obs_no_overlap: Literal["ignore", "error", "warn"] = "error", ) -> Comparer: ... @@ -188,8 +202,8 @@ def match( obs: Iterable[ObsTypes], mod: MRTypes | Sequence[MRTypes], *, - max_model_gap: Optional[float] = None, - spatial_method: Optional[str] = None, + max_model_gap: float | None = None, + spatial_method: str | None = None, spatial_tolerance: float = 1e-3, obs_no_overlap: Literal["ignore", "error", "warn"] = "error", ) -> ComparerCollection: ... @@ -200,7 +214,7 @@ def match( mod, *, max_model_gap=None, - spatial_method: Optional[str] = None, + spatial_method: str | None = None, spatial_tolerance: float = 1e-3, obs_no_overlap: Literal["ignore", "error", "warn"] = "error", ): @@ -248,6 +262,7 @@ def match( -------- from_matched - Create a Comparer from observation and model results that are already matched """ + if isinstance(obs, get_args(ObsInputType)): return _match_single_obs( obs, @@ -267,16 +282,23 @@ def match( if len(obs) > 1 and isinstance(mod, Collection) and len(mod) > 1: if not all( - isinstance(m, (DfsuModelResult, GridModelResult, DummyModelResult)) + isinstance( + m, + ( + DfsuModelResult, + GridModelResult, + NetworkModelResult, + DummyModelResult, + ), + ) for m in mod ): raise ValueError( """ - In case of multiple observations, multiple models can _only_ - be matched if they are _all_ of SpatialField type, e.g. DfsuModelResult - or GridModelResult. + When matching multiple observations with multiple models, all models + must be one of the following types: DfsuModelResult, GridModelResult or NetworkModelResult. - If you want match multiple point observations with multiple point model results, + If you want to match multiple point observations with multiple point model results, please match one observation at a time and then create a collection of these using modelskill.ComparerCollection(cmp_list) afterwards. The same applies to track data. """ @@ -317,14 +339,19 @@ def _match_single_obs( if len(names) != len(set(names)): raise ValueError(f"Duplicate model names found: {names}") - raw_mod_data = { - m.name: ( - m.extract(obs, spatial_method=spatial_method) - if isinstance(m, (DfsuModelResult, GridModelResult, DummyModelResult)) - else m - ) - for m in models - } + raw_mod_data: dict[str, PointModelResult | TrackModelResult | NodeModelResult] = {} + for m in models: + is_field = isinstance(m, (GridModelResult, DfsuModelResult)) + is_dummy = isinstance(m, DummyModelResult) + is_network = isinstance(m, NetworkModelResult) + if is_field or is_dummy: + matching_obs = m.extract(obs, spatial_method=spatial_method) + elif is_network: + matching_obs = m.extract(obs) + else: + matching_obs = m + + raw_mod_data[m.name] = matching_obs matched_data = _match_space_time( observation=obs, @@ -341,7 +368,7 @@ def _match_single_obs( def _get_global_start_end(idxs: Iterable[pd.DatetimeIndex]) -> Period: - assert all([len(x) > 0 for x in idxs]) + assert all([len(x) > 0 for x in idxs]), "All datetime indices must be non-empty" starts = [x[0] for x in idxs] ends = [x[-1] for x in idxs] @@ -351,11 +378,11 @@ def _get_global_start_end(idxs: Iterable[pd.DatetimeIndex]) -> Period: def _match_space_time( observation: Observation, - raw_mod_data: Mapping[str, PointModelResult | TrackModelResult], + raw_mod_data: Mapping[str, PointModelResult | TrackModelResult | NodeModelResult], max_model_gap: float | None, spatial_tolerance: float, obs_no_overlap: Literal["ignore", "error", "warn"], -) -> Optional[xr.Dataset]: +) -> xr.Dataset | None: idxs = [m.time for m in raw_mod_data.values()] period = _get_global_start_end(idxs) @@ -374,7 +401,10 @@ def _match_space_time( observation, spatial_tolerance=spatial_tolerance ) case PointModelResult() as pmr, PointObservation(): - aligned = pmr.align(observation, max_gap=max_model_gap) + aligned = align_data(pmr.data, observation, max_gap=max_model_gap) + case NodeModelResult() as nmr, NodeObservation(): + # mr is the extracted NodeModelResult + aligned = align_data(nmr.data, observation, max_gap=max_model_gap) case _: raise TypeError( f"Matching not implemented for model type {type(mr)} and observation type {type(observation)}" diff --git a/src/modelskill/metrics.py b/src/modelskill/metrics.py index 30e4b02bc..b195fddc6 100644 --- a/src/modelskill/metrics.py +++ b/src/modelskill/metrics.py @@ -11,7 +11,6 @@ Iterable, List, Literal, - Optional, Set, Tuple, TypeVar, @@ -43,7 +42,7 @@ def add_metric(metric: Callable, has_units: bool = False) -> None: def metric( best: Literal["+", "-", 0, 1] | None = None, has_units: bool = False, - display_name: Optional[str] = None, + display_name: str | None = None, ) -> Callable[[F], F]: """Decorator to indicate a function as a metric. @@ -101,7 +100,7 @@ def max_error(obs: ArrayLike, model: ArrayLike) -> Any: @metric(best="-", has_units=True) -def mae(obs: ArrayLike, model: ArrayLike, weights: Optional[ArrayLike] = None) -> Any: +def mae(obs: ArrayLike, model: ArrayLike, weights: ArrayLike | None = None) -> Any: """alias for mean_absolute_error""" assert obs.size == model.size return mean_absolute_error(obs, model, weights) @@ -109,7 +108,7 @@ def mae(obs: ArrayLike, model: ArrayLike, weights: Optional[ArrayLike] = None) - @metric(best="-", has_units=True) def mean_absolute_error( - obs: ArrayLike, model: ArrayLike, weights: Optional[ArrayLike] = None + obs: ArrayLike, model: ArrayLike, weights: ArrayLike | None = None ) -> Any: r"""Mean Absolute Error (MAE) @@ -155,7 +154,7 @@ def mean_absolute_percentage_error(obs: ArrayLike, model: ArrayLike) -> Any: @metric(best="-", has_units=True) -def urmse(obs: ArrayLike, model: ArrayLike, weights: Optional[ArrayLike] = None) -> Any: +def urmse(obs: ArrayLike, model: ArrayLike, weights: ArrayLike | None = None) -> Any: r"""Unbiased Root Mean Squared Error (uRMSE) $$ @@ -183,7 +182,7 @@ def urmse(obs: ArrayLike, model: ArrayLike, weights: Optional[ArrayLike] = None) def rmse( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, unbiased: bool = False, ) -> Any: """alias for root_mean_squared_error""" @@ -194,7 +193,7 @@ def rmse( def root_mean_squared_error( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, unbiased: bool = False, ) -> Any: r"""Root Mean Squared Error (RMSE) @@ -985,7 +984,7 @@ def c_max_error(obs: ArrayLike, model: ArrayLike) -> Any: def c_mean_absolute_error( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, ) -> Any: """Circular mean absolute error @@ -1016,7 +1015,7 @@ def c_mean_absolute_error( def c_mae( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, ) -> Any: """alias for circular mean absolute error""" return c_mean_absolute_error(obs, model, weights) @@ -1026,7 +1025,7 @@ def c_mae( def c_root_mean_squared_error( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, ) -> Any: """Circular root mean squared error @@ -1056,7 +1055,7 @@ def c_root_mean_squared_error( def c_rmse( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, ) -> Any: """alias for circular root mean squared error""" return c_root_mean_squared_error(obs, model, weights) @@ -1066,7 +1065,7 @@ def c_rmse( def c_unbiased_root_mean_squared_error( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, ) -> Any: """Circular unbiased root mean squared error @@ -1099,7 +1098,7 @@ def c_unbiased_root_mean_squared_error( def c_urmse( obs: ArrayLike, model: ArrayLike, - weights: Optional[ArrayLike] = None, + weights: ArrayLike | None = None, ) -> Any: """alias for circular unbiased root mean squared error""" return c_unbiased_root_mean_squared_error(obs, model, weights) diff --git a/src/modelskill/model/__init__.py b/src/modelskill/model/__init__.py index b0ba468ae..3390a5430 100644 --- a/src/modelskill/model/__init__.py +++ b/src/modelskill/model/__init__.py @@ -9,6 +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 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). """ @@ -20,6 +21,8 @@ from .track import TrackModelResult from .dfsu import DfsuModelResult from .grid import GridModelResult +from .network import NetworkModelResult +from .network import NodeModelResult from .dummy import DummyModelResult __all__ = [ @@ -27,6 +30,8 @@ "TrackModelResult", "DfsuModelResult", "GridModelResult", + "NetworkModelResult", + "NodeModelResult", "model_result", "DummyModelResult", ] diff --git a/src/modelskill/model/_base.py b/src/modelskill/model/_base.py index 250dcc30d..77c69416f 100644 --- a/src/modelskill/model/_base.py +++ b/src/modelskill/model/_base.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections import Counter from collections.abc import Hashable -from typing import List, Optional, Protocol, Sequence, TYPE_CHECKING +from typing import List, Protocol, Sequence, TYPE_CHECKING from dataclasses import dataclass import warnings @@ -28,7 +28,7 @@ def all(self) -> List[str]: def parse( avail_items: Sequence[Hashable], item: int | str | None, - aux_items: Optional[Sequence[int | str]] = None, + aux_items: Sequence[int | str] | None = None, ) -> SelectedItems: return _parse_items(avail_items, item, aux_items) @@ -36,7 +36,7 @@ def parse( def _parse_items( avail_items: Sequence[Hashable], item: int | str | None, - aux_items: Optional[Sequence[int | str]] = None, + aux_items: Sequence[int | str] | None = None, ) -> SelectedItems: """If input has exactly 1 item we accept item=None""" if item is None: @@ -76,13 +76,13 @@ class SpatialField(Protocol): def extract( self, observation: PointObservation | TrackObservation, - spatial_method: Optional[str] = None, + spatial_method: str | None = None, ) -> PointModelResult | TrackModelResult: ... def _extract_point( - self, observation: PointObservation, spatial_method: Optional[str] = None + self, observation: PointObservation, spatial_method: str | None = None ) -> PointModelResult: ... def _extract_track( - self, observation: TrackObservation, spatial_method: Optional[str] = None + self, observation: TrackObservation, spatial_method: str | None = None ) -> TrackModelResult: ... diff --git a/src/modelskill/model/adapters/__init__.py b/src/modelskill/model/adapters/__init__.py new file mode 100644 index 000000000..33ad255b1 --- /dev/null +++ b/src/modelskill/model/adapters/__init__.py @@ -0,0 +1 @@ +"""Network format adapters.""" diff --git a/src/modelskill/model/adapters/_res1d.py b/src/modelskill/model/adapters/_res1d.py new file mode 100644 index 000000000..8dc0c5ad9 --- /dev/null +++ b/src/modelskill/model/adapters/_res1d.py @@ -0,0 +1,107 @@ +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, EdgeBreakPoint, NetworkEdge + + +def _simplify_res1d_colnames(node: ResultNode | ResultGridPoint) -> pd.DataFrame: + # We remove suffixes and indexes so the columns contain only the quantity names + df = node.to_dataframe() + quantities = node.quantities + renamer_dict = {} + for quantity in quantities: + relevant_columns = [col for col in df.columns if quantity in col] + if len(relevant_columns) != 1: + raise ValueError( + f"There must be exactly one column per quantity, found {relevant_columns}." + ) + renamer_dict[relevant_columns[0]] = quantity + return df.rename(columns=renamer_dict).copy() + + +class Res1DNode(NetworkNode): + def __init__(self, node: ResultNode, boundary: dict[str, ResultGridPoint]): + self._id = node.id + self._data = _simplify_res1d_colnames(node) + self._boundary = { + key: _simplify_res1d_colnames(point) for key, point in boundary.items() + } + + @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(EdgeBreakPoint): + def __init__(self, point: ResultGridPoint): + self._id = (point.reach_name, point.chainage) + self._data = _simplify_res1d_colnames(point) + + @property + def id(self) -> tuple[str, float]: + return self._id + + @property + def data(self) -> pd.DataFrame: + return self._data + + +class Res1DReach(NetworkEdge): + """NetworkEdge adapter for a mikeio1d ResultReach.""" + + def __init__( + self, reach: ResultReach, start_node: ResultNode, end_node: ResultNode + ): + self._id = reach.name + + if start_node.id != reach.start_node: + raise ValueError("Incorrect starting node.") + if end_node.id != reach.end_node: + raise ValueError("Incorrect ending node.") + + start_gridpoint = reach.gridpoints[0] + end_gridpoint = reach.gridpoints[-1] + intermediate_gridpoints = ( + reach.gridpoints[1:-1] if len(reach.gridpoints) > 2 else [] + ) + + self._start = Res1DNode(start_node, {reach.name: start_gridpoint}) + self._end = Res1DNode(end_node, {reach.name: end_gridpoint}) + self._length = reach.length + self._breakpoints: list[EdgeBreakPoint] = [ + GridPoint(gridpoint) 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: + return self._length + + @property + def breakpoints(self) -> list[EdgeBreakPoint]: + return self._breakpoints diff --git a/src/modelskill/model/dfsu.py b/src/modelskill/model/dfsu.py index 8c4538b4a..70902dbed 100644 --- a/src/modelskill/model/dfsu.py +++ b/src/modelskill/model/dfsu.py @@ -1,7 +1,7 @@ from __future__ import annotations import inspect from pathlib import Path -from typing import Literal, Optional, get_args, cast +from typing import Literal, get_args, cast import mikeio import numpy as np @@ -23,7 +23,7 @@ class DfsuModelResult(SpatialField): ---------- data : types.UnstructuredType the input data or file path - name : Optional[str], optional + name : str | None, optional The name of the model result, by default None (will be set to file name or item name) item : str | int | None, optional @@ -31,7 +31,7 @@ class DfsuModelResult(SpatialField): 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 : Optional[list[int | str]], optional + aux_items : list[int | str] | None, optional Auxiliary items, by default None """ @@ -39,10 +39,10 @@ def __init__( self, data: UnstructuredType, *, - name: Optional[str] = None, - item: str | int | None = None, - quantity: Optional[Quantity] = None, - aux_items: Optional[list[int | str]] = None, + name: str | None = None, + item: int | str | None = None, + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, ) -> None: filename = None @@ -114,7 +114,7 @@ def _in_domain(self, x: float, y: float) -> bool: return self.data.geometry.contains([x, y]) # type: ignore def extract( - self, observation: Observation, spatial_method: Optional[str] = None + self, observation: Observation, spatial_method: str | None = None ) -> PointModelResult | TrackModelResult: """Extract ModelResult at observation positions @@ -124,7 +124,7 @@ def extract( ---------- observation : or positions (and times) at which modelresult should be extracted - spatial_method : Optional[str], optional + spatial_method : str | None, optional spatial selection/interpolation method, 'contained' (=isel), 'nearest', 'inverse_distance' (with 5 nearest points), by default None = 'inverse_distance' @@ -165,7 +165,7 @@ def _parse_spatial_method(method: str | None) -> str | None: return METHOD_MAP[method] def _extract_point( - self, observation: PointObservation, spatial_method: Optional[str] = None + self, observation: PointObservation, spatial_method: str | None = None ) -> PointModelResult: """Extract point. @@ -244,7 +244,7 @@ def _check_interpolation_method( ) def _extract_track( - self, observation: TrackObservation, spatial_method: Optional[str] = None + self, observation: TrackObservation, spatial_method: str | None = None ) -> TrackModelResult: """Extract track. diff --git a/src/modelskill/model/dummy.py b/src/modelskill/model/dummy.py index 6a1efea0d..a80c60d08 100644 --- a/src/modelskill/model/dummy.py +++ b/src/modelskill/model/dummy.py @@ -1,6 +1,6 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Literal, Optional +from typing import Literal import pandas as pd @@ -50,7 +50,7 @@ def __post_init__(self): def extract( self, observation: PointObservation | TrackObservation, - spatial_method: Optional[str] = None, + spatial_method: str | None = None, ) -> PointModelResult | TrackModelResult: if spatial_method is not None: raise NotImplementedError( diff --git a/src/modelskill/model/factory.py b/src/modelskill/model/factory.py index 4ee2cb7f3..12478f1aa 100644 --- a/src/modelskill/model/factory.py +++ b/src/modelskill/model/factory.py @@ -1,6 +1,6 @@ from __future__ import annotations from pathlib import Path -from typing import Literal, Optional, Any +from typing import Literal, Any import pandas as pd import xarray as xr @@ -24,8 +24,8 @@ def model_result( data: DataInputType, *, - aux_items: Optional[list[int | str]] = None, - gtype: Optional[Literal["point", "track", "unstructured", "grid"]] = None, + aux_items: list[int | str] | None = None, + gtype: Literal["point", "track", "unstructured", "grid"] | None = None, **kwargs: Any, ) -> PointModelResult | TrackModelResult | DfsuModelResult | GridModelResult: """A factory function for creating an appropriate object based on the data input. @@ -34,9 +34,9 @@ def model_result( ---------- data : DataInputType The data to be used for creating the ModelResult object. - aux_items : Optional[list[int | str]] + aux_items : list[int | str] | None Auxiliary items, by default None - gtype : Optional[Literal["point", "track", "unstructured", "grid"]] + gtype : Literal["point", "track", "unstructured", "grid"] | 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. diff --git a/src/modelskill/model/grid.py b/src/modelskill/model/grid.py index 67b88b35e..e3a8f6695 100644 --- a/src/modelskill/model/grid.py +++ b/src/modelskill/model/grid.py @@ -1,6 +1,6 @@ from __future__ import annotations from pathlib import Path -from typing import Optional, Sequence, get_args +from typing import Sequence, get_args import mikeio import pandas as pd @@ -30,7 +30,7 @@ class GridModelResult(SpatialField): 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 : Optional[list[int | str]], optional + aux_items : list[int | str] | None, optional Auxiliary items, by default None """ @@ -38,10 +38,10 @@ def __init__( self, data: GridType, *, - name: Optional[str] = None, - item: str | int | None = None, - quantity: Optional[Quantity] = None, - aux_items: Optional[list[int | str]] = None, + name: str | None = None, + item: int | str | None = None, + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, ) -> None: assert isinstance( data, get_args(GridType) @@ -125,7 +125,7 @@ def _in_domain(self, x: float, y: float) -> bool: def extract( self, observation: PointObservation | TrackObservation, - spatial_method: Optional[str] = None, + spatial_method: str | None = None, ) -> PointModelResult | TrackModelResult: """Extract ModelResult at observation positions @@ -135,7 +135,7 @@ def extract( ---------- observation : or positions (and times) at which modelresult should be extracted - spatial_method : Optional[str], optional + spatial_method : str | None, optional method in xarray.Dataset.interp, typically either "nearest" or "linear", by default None = 'linear' @@ -155,7 +155,7 @@ def extract( ) def _extract_point( - self, observation: PointObservation, spatial_method: Optional[str] = None + self, observation: PointObservation, spatial_method: str | None = None ) -> PointModelResult: """Extract point. @@ -204,7 +204,7 @@ def _extract_point( ) def _extract_track( - self, observation: TrackObservation, spatial_method: Optional[str] = None + self, observation: TrackObservation, spatial_method: str | None = None ) -> TrackModelResult: """Extract track. diff --git a/src/modelskill/model/network.py b/src/modelskill/model/network.py new file mode 100644 index 000000000..25b88e21c --- /dev/null +++ b/src/modelskill/model/network.py @@ -0,0 +1,197 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, 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 ._base import SelectedItems +from ..obs import NodeObservation +from ..quantity import Quantity +from ..types import PointType + +if TYPE_CHECKING: + from modelskill.network import Network + + +class NodeModelResult(TimeSeries): + """Model result for a single network node. + + Construct a NodeModelResult from timeseries data for a specific node. + This is a simple timeseries class designed for network node data. + + 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 + -------- + >>> import modelskill as ms + >>> mr = ms.NodeModelResult(data, node=123, name="Node_123") + >>> mr2 = ms.NodeModelResult(df, item="Water Level", node=456) + """ + + 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, + ) + + 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") + 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 _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) + + +class NetworkModelResult: + """Model result for network data with time and node dimensions. + + 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. + + Parameters + ---------- + data : Network + Network-like object with a ``to_dataset()`` method (e.g. :class:`modelskill.network.Network`). + name : str, optional + The name of the model result, + by default None (will be set to first data variable 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 + quantity : Quantity, optional + Model quantity + aux_items : list[int | str], optional + Auxiliary items, by default None + + Examples + -------- + >>> import modelskill as ms + >>> from modelskill.network import Network + >>> network = Network(edges) # edges is a list[NetworkEdge] + >>> mr = ms.NetworkModelResult(network, name="MyModel") + >>> obs = ms.NodeObservation(data, node=network.find(node="node_A")) + >>> extracted = mr.extract(obs) + """ + + def __init__( + self, + data: Network, + *, + name: str | None = None, + item: str | int | None = None, + quantity: Quantity | None = None, + aux_items: Sequence[int | str] | None = None, + ): + ds = data.to_dataset() + sel_items = SelectedItems.parse( + list(ds.data_vars), 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) + 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}" + + @property + def time(self) -> pd.DatetimeIndex: + """Return the time coordinate as a pandas.DatetimeIndex.""" + return pd.DatetimeIndex(self.data.time.to_index()) + + @property + def nodes(self) -> npt.NDArray[np.intp]: + """Return the node IDs as a numpy array of integers.""" + return self.data.node.values + + def extract( + self, + observation: NodeObservation, + ) -> NodeModelResult: + """Extract ModelResult at exact node locations + + Parameters + ---------- + observation : NodeObservation + observation with node ID (only NodeObservation supported) + + Returns + ------- + NodeModelResult + extracted model result + """ + if not isinstance(observation, NodeObservation): + raise TypeError( + f"NetworkModelResult only supports NodeObservation, got {type(observation).__name__}" + ) + + node_id = observation.node + if node_id not in self.data.node: + raise ValueError( + f"Node {node_id} not found. Available: {list(self.nodes[:5])}..." + ) + + 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, + ) diff --git a/src/modelskill/model/point.py b/src/modelskill/model/point.py index 5a11b1a30..4e071c4e9 100644 --- a/src/modelskill/model/point.py +++ b/src/modelskill/model/point.py @@ -1,14 +1,13 @@ from __future__ import annotations -from typing import Optional, Sequence, Any -import numpy as np +from typing import Sequence, Any import xarray as xr -import pandas as pd from ..obs import Observation from ..types import PointType from ..quantity import Quantity from ..timeseries import TimeSeries, _parse_xyz_point_input +from ..timeseries._align import align_data class PointModelResult(TimeSeries): @@ -22,7 +21,7 @@ class PointModelResult(TimeSeries): ---------- 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 : Optional[str], optional + name : str | None, optional The name of the model result, by default None (will be set to file name or item name) x : float, optional @@ -36,7 +35,7 @@ class PointModelResult(TimeSeries): 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 : Optional[list[int | str]], optional + aux_items : list[int | str] | None, optional Auxiliary items, by default None """ @@ -44,13 +43,13 @@ def __init__( self, data: PointType, *, - name: Optional[str] = None, - x: Optional[float] = None, - y: Optional[float] = None, - z: Optional[float] = None, + name: str | None = None, + x: float | None = None, + y: float | None = None, + z: float | None = None, item: str | int | None = None, - quantity: Optional[Quantity] = None, - aux_items: Optional[Sequence[int | str]] = None, + quantity: Quantity | None = None, + aux_items: Sequence[int | str] | None = None, ) -> None: if not self._is_input_validated(data): data = _parse_xyz_point_input( @@ -89,62 +88,5 @@ def interp_time(self, observation: Observation, **kwargs: Any) -> PointModelResu PointModelResult Interpolated model result """ - ds = self.align(observation, **kwargs) + ds = align_data(self.data, observation, **kwargs) return PointModelResult(ds) - - def align( - self, - observation: Observation, - *, - max_gap: float | None = None, - **kwargs: Any, - ) -> xr.Dataset: - new_time = observation.time - - dati = self.data.dropna("time").interp( - time=new_time, assume_sorted=True, **kwargs - ) - - pmr = PointModelResult(dati) - if max_gap is not None: - pmr = pmr._remove_model_gaps(mod_index=self.time, max_gap=max_gap) - return pmr.data - - def _remove_model_gaps( - self, - mod_index: pd.DatetimeIndex, - max_gap: float | None = None, - ) -> PointModelResult: - """Remove model gaps longer than max_gap from TimeSeries""" - max_gap_delta = pd.Timedelta(max_gap, "s") - valid_times = self._get_valid_times(mod_index, max_gap_delta) - ds = self.data.sel(time=valid_times) - return PointModelResult(ds) - - def _get_valid_times( - self, mod_index: pd.DatetimeIndex, max_gap: pd.Timedelta - ) -> pd.DatetimeIndex: - """Used only by _remove_model_gaps""" - obs_index = self.time - # init dataframe of available timesteps and their index - df = pd.DataFrame(index=mod_index) - df["idx"] = range(len(df)) - - # for query times get available left and right index of source times - df = ( - df.reindex(df.index.union(obs_index)) - .interpolate(method="time", limit_area="inside") - .reindex(obs_index) - .dropna() - ) - df["idxa"] = np.floor(df.idx).astype(int) - df["idxb"] = np.ceil(df.idx).astype(int) - - # time of left and right source times and time delta - df["ta"] = mod_index[df.idxa] - df["tb"] = mod_index[df.idxb] - df["dt"] = df.tb - df.ta - - # valid query times where time delta is less than max_gap - valid_idx = df.dt <= max_gap - return df[valid_idx].index diff --git a/src/modelskill/model/track.py b/src/modelskill/model/track.py index cb37ca8e7..612cb0063 100644 --- a/src/modelskill/model/track.py +++ b/src/modelskill/model/track.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Literal, Optional, Sequence +from typing import Literal, Sequence import warnings import numpy as np @@ -21,7 +21,7 @@ class TrackModelResult(TimeSeries): ---------- data : types.TrackType The input data or file path - name : Optional[str], optional + name : str | None, optional The name of the model result, by default None (will be set to file name or item name) item : str | int | None, optional @@ -38,7 +38,7 @@ class TrackModelResult(TimeSeries): "first" to keep first occurrence, "last" to keep last occurrence, False to drop all duplicates, "offset" to add milliseconds to consecutive duplicates, by default "first" - aux_items : Optional[list[int | str]], optional + aux_items : list[int | str] | None, optional Auxiliary items, by default None """ @@ -46,13 +46,13 @@ def __init__( self, data: TrackType, *, - name: Optional[str] = None, + name: str | None = None, item: str | int | None = None, - quantity: Optional[Quantity] = None, + quantity: Quantity | None = None, x_item: str | int = 0, y_item: str | int = 1, keep_duplicates: Literal["first", "last", False] = "first", - aux_items: Optional[Sequence[int | str]] = None, + aux_items: Sequence[int | str] | None = None, ) -> None: if not self._is_input_validated(data): data = _parse_track_input( diff --git a/src/modelskill/network.py b/src/modelskill/network.py new file mode 100644 index 000000000..ae3f492e0 --- /dev/null +++ b/src/modelskill/network.py @@ -0,0 +1,701 @@ +"""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 + +from abc import ABC, abstractmethod +from pathlib import Path +from typing import Any, Sequence, overload, TYPE_CHECKING + +import networkx as nx +import pandas as pd +import xarray as xr + +if TYPE_CHECKING: + from mikeio1d import Res1D + + +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. + NetworkEdge : Connects two NetworkNode instances. + Network : Container that assembles nodes and edges 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 EdgeBreakPoint(ABC): + """Abstract base class for an intermediate break point along a network edge. + + Break points represent locations between the start and end nodes of an + edge (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 ``(edge_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-edge distance in the units used by the parent network). + + Examples + -------- + Minimal subclass: + + >>> class MyBreakPoint(EdgeBreakPoint): + ... def __init__(self, edge_id, chainage, df): + ... self._id = (edge_id, chainage) + ... self._data = df + ... @property + ... def id(self): return self._id + ... @property + ... def data(self): return self._data + + See Also + -------- + NetworkEdge : Owns a list of EdgeBreakPoint instances. + NetworkNode : Represents a start/end node of an edge. + Network : Assembles edges (and their break points) into a graph. + """ + + @property + @abstractmethod + def id(self) -> tuple[str, float]: + """``(edge_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-edge distance of this break point (same units as :attr:`NetworkEdge.length`).""" + 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 NetworkEdge(ABC): + """Abstract base class for an edge in a network. + + An edge 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:`EdgeBreakPoint` objects for intermediate chainage + locations. + + Subclass this to integrate your own network topology. Five properties + must be implemented: + + * :attr:`id` - a unique string identifier for the edge. + * :attr:`start` - the upstream/start :class:`NetworkNode`. + * :attr:`end` - the downstream/end :class:`NetworkNode`. + * :attr:`length` - total edge length (in the units of your coordinate + system). + * :attr:`breakpoints` - list of :class:`EdgeBreakPoint` instances ordered + by increasing distance from the start node (empty list if none). + + The concrete helper :class:`BasicEdge` is provided for the common case + where all data is already available in memory. + + Examples + -------- + Minimal subclass: + + >>> class MyEdge(NetworkEdge): + ... def __init__(self, eid, start_node, end_node, length): + ... self._id = eid + ... self._start = start_node + ... self._end = end_node + ... self._length = length + ... @property + ... def id(self): return self._id + ... @property + ... def start(self): return self._start + ... @property + ... def end(self): return self._end + ... @property + ... def length(self): return self._length + ... @property + ... def breakpoints(self): return [] + + See Also + -------- + BasicEdge : Ready-to-use concrete implementation. + NetworkNode : Represents the start/end of this edge. + EdgeBreakPoint : Intermediate data points along this edge. + Network : Assembles a list of NetworkEdge objects into a graph. + """ + + @property + @abstractmethod + def id(self) -> str: + """Unique string identifier for this edge.""" + pass + + @property + @abstractmethod + def start(self) -> NetworkNode: + """Start (upstream) node of this edge.""" + pass + + @property + @abstractmethod + def end(self) -> NetworkNode: + """End (downstream) node of this edge.""" + pass + + @property + @abstractmethod + def length(self) -> float: + """Total length of this edge in network units.""" + pass + + @property + @abstractmethod + def breakpoints(self) -> list[EdgeBreakPoint]: + """Ordered list of intermediate :class:`EdgeBreakPoint` objects (may be empty).""" + pass + + @property + def n_breakpoints(self) -> int: + """Number of break points in the edge.""" + 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 BasicEdge(NetworkEdge): + """Concrete :class:`NetworkEdge` for programmatic network construction. + + Parameters + ---------- + id : str + Unique edge identifier. + start : NetworkNode + Start node. + end : NetworkNode + End node. + length : float + Edge length. + breakpoints : list[EdgeBreakPoint], optional + Intermediate break points, by default empty. + + Examples + -------- + >>> edge = BasicEdge("reach_1", node_a, node_b, length=250.0) + """ + + def __init__( + self, + id: str, + start: NetworkNode, + end: NetworkNode, + length: float, + breakpoints: list[EdgeBreakPoint] | None = None, + ) -> None: + self._id = id + self._start = start + self._end = end + self._length = length + self._breakpoints: list[EdgeBreakPoint] = 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[EdgeBreakPoint]: + return self._breakpoints + + +class Network: + """Network built from a set of edges, with coordinate lookup and data access.""" + + def __init__(self, edges: Sequence[NetworkEdge]): + self._edges: dict[str, NetworkEdge] = {e.id: e for e in edges} + self._graph = self._initialize_graph() + self._alias_map = self._initialize_alias_map() + self._df = self._build_dataframe() + + def __repr__(self) -> str: + time = self._df.index + out = [ + "", + f"Edges: {len(self._edges)}", + f"Nodes: {self._graph.number_of_nodes()}", + f"Quantities: {self.quantities}", + f"Time: {time[0]} - {time[-1]}", + ] + return "\n".join(out) + + @classmethod + def from_res1d(cls, res: str | Path | Res1D) -> Network: + """Create a Network from a Res1D file or object. + + Parameters + ---------- + res : str, Path or Res1D + Path to a .res1d file, or an already-opened :class:`mikeio1d.Res1D` object. + + Returns + ------- + Network + + Examples + -------- + >>> from modelskill.network import Network + >>> network = Network.from_res1d("model.res1d") + >>> network = Network.from_res1d(Res1D("model.res1d")) + """ + from mikeio1d import Res1D as _Res1D + from modelskill.model.adapters._res1d import Res1DReach + + if isinstance(res, (str, Path)): + path = Path(res) + if path.suffix.lower() != ".res1d": + raise NotImplementedError( + f"Unsupported file extension '{path.suffix}'. Only .res1d files are supported." + ) + res = _Res1D(str(path)) + elif not isinstance(res, _Res1D): + raise TypeError( + f"Expected a str, Path or Res1D object, got {type(res).__name__!r}" + ) + + edges = [ + Res1DReach(reach, res.nodes[reach.start_node], res.nodes[reach.end_node]) + for reach in res.reaches.values() + ] + return cls(edges) + + def _initialize_alias_map(self) -> dict[str | tuple[str, float], int]: + return {self.graph.nodes[id]["alias"]: id for id in self.graph.nodes()} + + def _build_dataframe(self) -> pd.DataFrame: + df = pd.concat({k: v["data"] for k, v in self._graph.nodes.items()}, 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 = self.to_dataframe().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()) + + def _initialize_graph(self) -> nx.Graph: + g0 = nx.Graph() + for edge in self._edges.values(): + # 1) Add start and end nodes + for node in [edge.start, edge.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 = edge.start.id + end_key = edge.end.id + if edge.n_breakpoints == 0: + g0.add_edge(start_key, end_key, length=edge.length) + else: + bp_keys = [bp.id for bp in edge.breakpoints] + for bp, bp_key in zip(edge.breakpoints, bp_keys): + g0.add_node(bp_key, data=bp.data) + + g0.add_edge(start_key, bp_keys[0], length=edge.breakpoints[0].distance) + g0.add_edge( + bp_keys[-1], + end_key, + length=edge.length - edge.breakpoints[-1].distance, + ) + + # 3) Connect consecutive intermediate breakpoints + for i in range(edge.n_breakpoints - 1): + current_ = edge.breakpoints[i] + next_ = edge.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, + edge: None = None, + distance: None = None, + ) -> int: + pass + + @overload + def find( + self, + *, + node: list[str], + edge: None = None, + distance: None = None, + ) -> list[int]: + pass + + @overload + def find( + self, + *, + node: None = None, + edge: str | list[str], + distance: str | float, + ) -> int: + pass + + @overload + def find( + self, + *, + node: None = None, + edge: str | list[str], + distance: list[str | float], + ) -> list[int]: + pass + + def find( + self, + node: str | list[str] | None = None, + edge: 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 + edge : str | List[str], optional + Edge id(s) for breakpoint lookup or edge endpoint lookup, by default None + distance : str | float | List[str | float], optional + Distance(s) along edge for breakpoint lookup, or "start"/"end" + for edge 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 = edge is not None or distance is not None + + if by_node and by_breakpoint: + raise ValueError( + "Cannot specify both 'node' and 'edge'/'distance' parameters simultaneously" + ) + + if not by_node and not by_breakpoint: + raise ValueError( + "Must specify either 'node' or both 'edge' 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 edge is None or distance is None: + raise ValueError( + "Both 'edge' and 'distance' parameters are required for breakpoint/endpoint lookup" + ) + + if not isinstance(edge, list): + edge = [edge] + + if not isinstance(distance, list): + distance = [distance] + + if len(edge) == 1: + edge = edge * len(distance) + + if len(edge) != len(distance): + raise ValueError( + "Incompatible lengths of 'edge' and 'distance' arguments. One 'edge' admits multiple distances, otherwise they must be the same length." + ) + + ids = [] + for edge_i, distance_i in zip(edge, distance): + if distance_i in ["start", "end"]: + if edge_i not in self._edges: + raise KeyError(f"Edge '{edge_i}' not found in the network.") + + network_edge = self._edges[edge_i] + if distance_i == "start": + ids.append(network_edge.start.id) + else: + ids.append(network_edge.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((edge_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): + edge_id, distance = id + for key, val in self._alias_map.items(): + if ( + isinstance(key, tuple) + and key[0] == edge_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: 'edge' and 'distance' keys with edge 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({"edge": key[0], "distance": key[1]}) + + if len(results) == 1: + return results[0] + else: + return results + + +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) + ] + edges = [ + BasicEdge(f"e{i}", nodes[i], nodes[i + 1], length=100.0) + for i in range(len(nodes) - 1) + ] + return Network(edges) diff --git a/src/modelskill/obs.py b/src/modelskill/obs.py index 5828b2b03..f0c4e649e 100644 --- a/src/modelskill/obs.py +++ b/src/modelskill/obs.py @@ -1,17 +1,19 @@ """ # Observations -ModelSkill supports two types of observations: +ModelSkill supports three 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 +* [`NodeObservation`](`modelskill.NodeObservation`) - a network node timeseries for specific node IDs. 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, Optional, Any, Union +from typing import Literal, Any, Union, overload +from typing_extensions import Self import warnings import pandas as pd import xarray as xr @@ -22,6 +24,7 @@ TimeSeries, _parse_xyz_point_input, _parse_track_input, + _parse_network_node_input, ) # NetCDF attributes can only be str, int, float https://unidata.github.io/netcdf4-python/#attributes-in-a-netcdf-file @@ -31,9 +34,9 @@ def observation( data: DataInputType, *, - gtype: Optional[Literal["point", "track"]] = None, + gtype: Literal["point", "track", "node"] | None = None, **kwargs, -) -> PointObservation | TrackObservation: +) -> PointObservation | TrackObservation | NodeObservation: """Create an appropriate observation object. A factory function for creating an appropriate observation object @@ -41,19 +44,20 @@ def observation( If 'x' or 'y' is given, a PointObservation is created. If 'x_item' or 'y_item' is given, a TrackObservation is created. + If 'node' is given, a NodeObservation is created. Parameters ---------- data : DataInputType The data to be used for creating the Observation object. - gtype : Optional[Literal["point", "track"]] + gtype : Literal["point", "track", "node"] | 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 Observation constructor. Returns ------- - PointObservation or TrackObservation + PointObservation or TrackObservation or NodeObservation An observation object of the appropriate type Examples @@ -61,6 +65,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", node=123, name="123") """ if gtype is None: geometry = _guess_gtype(**kwargs) @@ -80,14 +85,16 @@ def _guess_gtype(**kwargs) -> GeometryType: return GeometryType.POINT elif "x_item" in kwargs or "y_item" in kwargs: return GeometryType.TRACK + elif "node" in kwargs: + return GeometryType.NODE else: warnings.warn( - "Could not guess geometry type from data or args, assuming POINT geometry. Use PointObservation or TrackObservation to be explicit." + "Could not guess geometry type from data or args, assuming POINT geometry. Use PointObservation, TrackObservation, or NodeObservation to be explicit." ) return GeometryType.POINT -def _validate_attrs(data_attrs: dict, attrs: Optional[dict]) -> None: +def _validate_attrs(data_attrs: dict, attrs: dict | None) -> None: # See similar method in xarray https://github.com/pydata/xarray/blob/main/xarray/backends/api.py#L165 if attrs is None: @@ -109,7 +116,7 @@ def __init__( data: xr.Dataset, weight: float, color: str = "#d62728", # TODO: cannot currently be set by user - attrs: Optional[dict] = None, + attrs: dict | None = None, ) -> None: assert isinstance(data, xr.Dataset) @@ -196,15 +203,15 @@ def __init__( self, data: PointType, *, - item: Optional[int | str] = None, - x: Optional[float] = None, - y: Optional[float] = None, - z: Optional[float] = None, - name: Optional[str] = None, + item: int | str | None = None, + x: float | None = None, + y: float | None = None, + z: float | None = None, + name: str | None = None, weight: float = 1.0, - quantity: Optional[Quantity] = None, - aux_items: Optional[list[int | str]] = None, - attrs: Optional[dict] = None, + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, ) -> None: if not self._is_input_validated(data): data = _parse_xyz_point_input( @@ -315,15 +322,15 @@ def __init__( self, data: TrackType, *, - item: Optional[int | str] = None, - name: Optional[str] = None, + item: int | str | None = None, + name: str | None = None, weight: float = 1.0, - x_item: Optional[int | str] = 0, - y_item: Optional[int | str] = 1, + x_item: int | str | None = 0, + y_item: int | str | None = 1, keep_duplicates: Literal["first", "last", False] = "first", - quantity: Optional[Quantity] = None, - aux_items: Optional[list[int | str]] = None, - attrs: Optional[dict] = None, + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, ) -> None: if not self._is_input_validated(data): data = _parse_track_input( @@ -340,6 +347,185 @@ def __init__( super().__init__(data=data, weight=weight, attrs=attrs) +class NodeObservation(Observation): + """Class for observations at network nodes. + + Create a NodeObservation from a DataFrame or other data source. + The node ID is specified as an integer. + + To create multiple NodeObservation objects from a single data source, + use :method:`from_multiple`. + + Parameters + ---------- + data : str, Path, mikeio.Dataset, mikeio.DataArray, pd.DataFrame, pd.Series, xr.Dataset or xr.DataArray + data source with time series for the node + 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 + node : int, optional + node ID (integer), by default None + name : str, optional + user-defined name for easy identification in plots etc, by default derived from data + weight : float, optional + weighting factor for skill scores, by default 1.0 + quantity : Quantity, optional + The quantity of the observation, for validation with model results + aux_items : list, optional + 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 + + Examples + -------- + >>> import modelskill as ms + >>> o1 = ms.NodeObservation(data, node=123, name="123") + >>> o2 = ms.NodeObservation(df, item="Water Level", node=456) + >>> + >>> # Multiple node observations from separate data sources + >>> obs = ms.NodeObservation.from_multiple(nodes={123: df1, 456: df2}) + """ + + def __init__( + self, + data: PointType, + node: int, + *, + 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, + ) -> 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, + ) + + assert isinstance(data, xr.Dataset) + super().__init__(data=data, weight=weight, attrs=attrs) + + @property + def node(self) -> int: + """Node ID of observation""" + node_val = self.data.coords["node"] + return int(node_val.item()) + + def _create_new_instance(self, data: xr.Dataset) -> Self: + """Extract node from data and create new instance""" + node = int(data.coords["node"].item()) + return self.__class__(data, node=node) + + @overload + @classmethod + def from_multiple( + cls, + *, + data: PointType, + nodes: dict[int, str | int], + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, + ) -> list[NodeObservation]: ... + + @overload + @classmethod + def from_multiple( + cls, + *, + nodes: dict[int, PointType], + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, + ) -> list[NodeObservation]: + pass + + @classmethod + def from_multiple( + cls, + *, + data: PointType | None = None, + nodes: dict[int, Any] | None = None, + quantity: Quantity | None = None, + aux_items: list[int | str] | None = None, + attrs: dict | None = None, + ) -> list[NodeObservation]: + """Create multiple NodeObservation objects. + + Two calling conventions are supported: + + 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"}) + + 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"}) + + 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 + 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[NodeObservation] + List of NodeObservation objects. + """ + if nodes is None: + raise ValueError("'nodes' argument is required") + if not isinstance(nodes, dict): + raise TypeError( + f"'nodes' must be a dict mapping node_id -> data_source, got {type(nodes).__name__}" + ) + + node_ids = list(nodes.keys()) + + if data is None: + data_sources: list[PointType] = list(nodes.values()) # type: ignore[list-item] + return [ + cls( + data_i, + node=node_i, + item=None, + quantity=quantity, + aux_items=aux_items, + attrs=attrs, + ) + for data_i, node_i in zip(data_sources, node_ids) + ] + else: + node_items: list[int | str | None] = list(nodes.values()) # type: ignore[list-item] + return [ + cls( + data, + node=node_i, + item=item_i, + quantity=quantity, + aux_items=aux_items, + attrs=attrs, + ) + for node_i, item_i in zip(node_ids, node_items) + ] + + def unit_display_name(name: str) -> str: """Display name @@ -364,4 +550,5 @@ def unit_display_name(name: str) -> str: _obs_class_lookup = { GeometryType.POINT: PointObservation, GeometryType.TRACK: TrackObservation, + GeometryType.NODE: NodeObservation, } diff --git a/src/modelskill/plotting/_misc.py b/src/modelskill/plotting/_misc.py index 4716fb799..e41a8e214 100644 --- a/src/modelskill/plotting/_misc.py +++ b/src/modelskill/plotting/_misc.py @@ -1,6 +1,6 @@ from __future__ import annotations import warnings -from typing import Optional, Sequence, Tuple, Union, Mapping +from typing import Sequence, Tuple, Union, Mapping import matplotlib.pyplot as plt from matplotlib.axes import Axes @@ -115,7 +115,7 @@ def sample_points( def quantiles_xy( x: np.ndarray, y: np.ndarray, - quantiles: Optional[Union[int, Sequence[float]]] = None, + quantiles: Union[int, Sequence[float]] | None = None, ): """Calculate quantiles of x and y diff --git a/src/modelskill/plotting/_scatter.py b/src/modelskill/plotting/_scatter.py index de2cd700a..e8d5c0600 100644 --- a/src/modelskill/plotting/_scatter.py +++ b/src/modelskill/plotting/_scatter.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Literal, Optional, Sequence, Tuple, Callable, TYPE_CHECKING, Mapping +from typing import Literal, Sequence, Tuple, Callable, TYPE_CHECKING, Mapping if TYPE_CHECKING: import matplotlib.axes @@ -29,21 +29,21 @@ def scatter( quantiles: int | Sequence[float] | None = None, fit_to_quantiles: bool = False, show_points: bool | int | float | None = None, - show_hist: Optional[bool] = None, - show_density: Optional[bool] = None, - norm: Optional[colors.Normalize] = None, + show_hist: bool | None = None, + show_density: bool | None = None, + norm: colors.Normalize | None = None, backend: Literal["matplotlib", "plotly"] = "matplotlib", figsize: Tuple[float, float] = (8, 8), - xlim: Optional[Tuple[float, float]] = None, - ylim: Optional[Tuple[float, float]] = None, + xlim: Tuple[float, float] | None = None, + ylim: Tuple[float, float] | None = None, reg_method: str | bool = "ols", title: str = "", xlabel: str = "", ylabel: str = "", - skill_table: Optional[str | Sequence[str] | Mapping[str, str] | bool] = False, + skill_table: str | Sequence[str] | Mapping[str, str] | bool | None = False, skill_scores: Mapping[str, float] | None = None, - skill_score_unit: Optional[str] = "", - ax: Optional[Axes] = None, + skill_score_unit: str | None = "", + ax: Axes | None = None, **kwargs, ) -> Axes: """Scatter plot tailored for model skill comparison. @@ -649,7 +649,7 @@ def _plot_summary_table( skill_scores: Mapping[str, float], units: str, ax, - cbar_width: Optional[float] = None, + cbar_width: float | None = None, ) -> None: # If colorbar, get extents from colorbar label: x0 = options.plot.scatter.skill_table.x_position diff --git a/src/modelskill/plotting/_spatial_overview.py b/src/modelskill/plotting/_spatial_overview.py index 156af04c1..276a94b63 100644 --- a/src/modelskill/plotting/_spatial_overview.py +++ b/src/modelskill/plotting/_spatial_overview.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Optional, Iterable, Tuple, TYPE_CHECKING +from typing import Iterable, Tuple, TYPE_CHECKING if TYPE_CHECKING: import matplotlib.axes @@ -14,15 +14,16 @@ def spatial_overview( obs: Observation | Iterable[Observation], - mod: Optional[ + mod: ( DfsuModelResult | GeometryFM2D | Iterable[DfsuModelResult] | Iterable[GeometryFM2D] - ] = None, + ) + | None = None, ax=None, - figsize: Optional[Tuple] = None, - title: Optional[str] = None, + figsize: Tuple | None = None, + title: str | None = None, ) -> matplotlib.axes.Axes: """Plot observation points on a map showing the model domain diff --git a/src/modelskill/plotting/_wind_rose.py b/src/modelskill/plotting/_wind_rose.py index b10e018bf..730f197ed 100644 --- a/src/modelskill/plotting/_wind_rose.py +++ b/src/modelskill/plotting/_wind_rose.py @@ -1,6 +1,6 @@ from __future__ import annotations from dataclasses import dataclass -from typing import List, Optional, Tuple, Union, TYPE_CHECKING +from typing import List, Tuple, Union, TYPE_CHECKING if TYPE_CHECKING: import matplotlib.axes @@ -387,10 +387,10 @@ def directional_labels(n: int) -> Tuple[str, ...]: def pretty_intervals( magmax: float, - mag_bins: Optional[List[float]] = None, - mag_step: Optional[float] = None, - vmin: Optional[float] = None, - max_bin: Optional[float] = None, + mag_bins: List[float] | None = None, + mag_step: float | None = None, + vmin: float | None = None, + max_bin: float | None = None, n_decimals: int = 3, ): """Pretty intervals for the magnitude bins""" @@ -497,7 +497,7 @@ def _calc_mag_step(magmax: float) -> float: return mag_step -def _calc_radial_ticks(*, counts: np.ndarray, step: float, stop: Optional[float]): +def _calc_radial_ticks(*, counts: np.ndarray, step: float, stop: float | None): cmax = counts.sum(axis=0).max() if stop is None: rmax = np.ceil((cmax + step) / step) * step diff --git a/src/modelskill/settings.py b/src/modelskill/settings.py index 3d4df4816..73e7063ba 100644 --- a/src/modelskill/settings.py +++ b/src/modelskill/settings.py @@ -74,7 +74,6 @@ Iterable, List, NamedTuple, - Optional, Dict, Tuple, Type, @@ -87,8 +86,8 @@ class RegisteredOption(NamedTuple): key: str defval: object doc: str - validator: Optional[Callable[[object], Any]] - # cb: Optional[Callable[[str], Any]] + validator: Callable[[object], Any] | None + # cb: Callable[[str], Any] | None # holds registered option metadata @@ -180,7 +179,7 @@ def _option_to_dict(pat: str = "") -> Dict: return d -def _describe_option_short(pat: str = "", _print_desc: bool = True) -> Optional[str]: +def _describe_option_short(pat: str = "", _print_desc: bool = True) -> str | None: keys = _select_options(pat) if len(keys) == 0: raise OptionError("No such keys(s)") @@ -193,7 +192,7 @@ def _describe_option_short(pat: str = "", _print_desc: bool = True) -> Optional[ return s -def _describe_option(pat: str = "", _print_desc: bool = True) -> Optional[str]: +def _describe_option(pat: str = "", _print_desc: bool = True) -> str | None: keys = _select_options(pat) if len(keys) == 0: raise OptionError("No such keys(s)") @@ -350,8 +349,8 @@ def register_option( key: str, defval: object, doc: str = "", - validator: Optional[Callable[[Any], Any]] = None, - # cb: Optional[Callable[[str], Any]] = None, + validator: Callable[[Any], Any] | None = None, + # cb: Callable[[str], Any] | None = None, ) -> None: """ Register an option in the package-wide modelskill settingss object diff --git a/src/modelskill/timeseries/__init__.py b/src/modelskill/timeseries/__init__.py index cb1ca3ffc..4885c9987 100644 --- a/src/modelskill/timeseries/__init__.py +++ b/src/modelskill/timeseries/__init__.py @@ -1,9 +1,10 @@ from ._timeseries import TimeSeries -from ._point import _parse_xyz_point_input +from ._point import _parse_xyz_point_input, _parse_network_node_input from ._track import _parse_track_input __all__ = [ "TimeSeries", "_parse_xyz_point_input", "_parse_track_input", + "_parse_network_node_input", ] diff --git a/src/modelskill/timeseries/_align.py b/src/modelskill/timeseries/_align.py new file mode 100644 index 000000000..5e0b6b1b8 --- /dev/null +++ b/src/modelskill/timeseries/_align.py @@ -0,0 +1,85 @@ +"""Time series alignment utilities.""" + +from __future__ import annotations +import numpy as np +import pandas as pd +import xarray as xr +from typing import Any +from ..obs import Observation + + +def _get_valid_times( + obs_time: pd.DatetimeIndex, mod_index: pd.DatetimeIndex, max_gap: pd.Timedelta +) -> pd.DatetimeIndex: + """Get valid times where interpolation gaps are within max_gap""" + # init dataframe of available timesteps and their index + df = pd.DataFrame(index=mod_index) + df["idx"] = range(len(df)) + + # for query times get available left and right index of source times + df = ( + df.reindex(df.index.union(obs_time)) + .interpolate(method="time", limit_area="inside") + .reindex(obs_time) + .dropna() + ) + df["idxa"] = np.floor(df.idx).astype(int) + df["idxb"] = np.ceil(df.idx).astype(int) + + # time of left and right source times and time delta + df["ta"] = mod_index[df.idxa] + df["tb"] = mod_index[df.idxb] + df["dt"] = df.tb - df.ta + + # valid query times where time delta is less than max_gap + valid_idx = df.dt <= max_gap + return df[valid_idx].index + + +def _remove_model_gaps( + data: xr.Dataset, + mod_index: pd.DatetimeIndex, + max_gap: float | None = None, +) -> xr.Dataset: + """Remove model gaps longer than max_gap from Dataset""" + max_gap_delta = pd.Timedelta(max_gap, "s") + obs_time = pd.DatetimeIndex(data.time.to_index()) + valid_times = _get_valid_times(obs_time, mod_index, max_gap_delta) + return data.sel(time=valid_times) + + +def align_data( + data: xr.Dataset, + observation: Observation, + *, + max_gap: float | None = None, + **kwargs: Any, +) -> xr.Dataset: + """Align model data to observation time. + + Interpolates model result to the time of the observation. + + Parameters + ---------- + data : xr.Dataset + The model dataset to align + observation : Observation + The observation to align to + max_gap : float | None, optional + Maximum gap in seconds for interpolation gaps removal, by default None + **kwargs : Any + Additional keyword arguments passed to xarray.interp + + Returns + ------- + xr.Dataset + Aligned dataset + """ + new_time = observation.time + + dati = data.dropna("time").interp(time=new_time, assume_sorted=True, **kwargs) + + if max_gap is not None: + model_time = pd.DatetimeIndex(data.time.to_index()) + dati = _remove_model_gaps(dati, mod_index=model_time, max_gap=max_gap) + return dati diff --git a/src/modelskill/timeseries/_coords.py b/src/modelskill/timeseries/_coords.py index 899dd5e63..5e5aa626a 100644 --- a/src/modelskill/timeseries/_coords.py +++ b/src/modelskill/timeseries/_coords.py @@ -1,14 +1,12 @@ import numpy as np -from typing import Optional - class XYZCoords: def __init__( self, - x: Optional[float] = None, - y: Optional[float] = None, - z: Optional[float] = None, + x: float | None = None, + y: float | None = None, + z: float | None = None, ): self.x = x if x is not None else np.nan self.y = y if y is not None else np.nan @@ -17,3 +15,17 @@ def __init__( @property def as_dict(self) -> dict: return {"x": self.x, "y": self.y, "z": self.z} + + +class NetworkCoords: + def __init__( + self, + node: int | None = None, + boundary: str | None = None, + ): + self.node = node if node is not None else np.nan + self.boundary = boundary if boundary is not None else np.nan + + @property + def as_dict(self) -> dict: + return {"node": self.node, "boundary": self.boundary} diff --git a/src/modelskill/timeseries/_point.py b/src/modelskill/timeseries/_point.py index 5e2a0c86a..a9fb076b6 100644 --- a/src/modelskill/timeseries/_point.py +++ b/src/modelskill/timeseries/_point.py @@ -2,10 +2,9 @@ from collections.abc import Hashable from dataclasses import dataclass from pathlib import Path -from typing import Sequence, get_args, List, Optional, Tuple, Union, Any +from typing import Sequence, get_args, List, Tuple, Union, Any import pandas as pd import xarray as xr -import numpy as np import mikeio @@ -13,7 +12,7 @@ from ..quantity import Quantity from ..utils import _get_name from ._timeseries import _validate_data_var_name -from ._coords import XYZCoords +from ._coords import XYZCoords, NetworkCoords @dataclass @@ -29,7 +28,7 @@ def all(self) -> List[str]: def _parse_point_items( items: Sequence[Hashable], item: int | str | None, - aux_items: Optional[Sequence[int | str]] = None, + aux_items: Sequence[int | str] | None = None, ) -> PointItem: """If input has exactly 1 item we accept item=None""" if item is None: @@ -62,8 +61,8 @@ def _select_items( pd.Series, pd.DataFrame, ], - item: Optional[str | int] = None, - aux_items: Optional[Sequence[int | str]] = None, + item: str | int | None = None, + aux_items: Sequence[int | str] | None = None, ) -> PointItem: if isinstance(data, (mikeio.DataArray, pd.Series, xr.DataArray)): item_name = data.name if data.name is not None else "PointModelResult" @@ -144,7 +143,7 @@ def _convert_to_dataset( def _include_coords( ds: xr.Dataset, *, - coords: Optional[XYZCoords] = None, + coords: XYZCoords | NetworkCoords | None = None, ) -> xr.Dataset: ds = ds.copy() if coords is not None: @@ -153,7 +152,9 @@ def _include_coords( coords_to_add = {} for k, v in coords.as_dict.items(): # Add if coordinate doesn't exist, or if user provided a non-null value - if k not in ds.coords or (v is not None and not np.isnan(v)): + # - pd.isna(v) returns True for NaN, False otherwise + # - for string values: pd.isna(v) returns False (strings are never considered "NA" unless specifically None) + if k not in ds.coords or (v is not None and not pd.isna(v)): coords_to_add[k] = v ds.coords.update(coords_to_add) @@ -165,7 +166,10 @@ def _include_attributes( ) -> xr.Dataset: ds = ds.copy() - ds.attrs["gtype"] = str(GeometryType.POINT) + if "node" in ds.coords: + ds.attrs["gtype"] = str(GeometryType.NODE) + else: + ds.attrs["gtype"] = str(GeometryType.POINT) ds[name].attrs["long_name"] = quantity.name ds[name].attrs["units"] = quantity.unit @@ -178,7 +182,7 @@ def _include_attributes( def _open_and_name( - data: PointType, name: Optional[str] + data: PointType, name: str | None ) -> Tuple[ Union[ mikeio.Dataset, @@ -204,6 +208,8 @@ def _open_and_name( elif suffix == ".nc": data = xr.open_dataset(data) name = name if name_is_arg else data.attrs.get("name") or stem + elif suffix == ".csv": + data = pd.read_csv(data, parse_dates=True, index_col=0) elif isinstance(data, mikeio.Dfs0): data = data.read() # now mikeio.Dataset @@ -232,14 +238,18 @@ def _select_variable_name(name: str, sel_items: PointItem) -> str: def _parse_point_input( data: PointType, - name: Optional[str], - item: Optional[str | int], - quantity: Optional[Quantity], - aux_items: Optional[Sequence[int | str]], + name: str | None, + item: str | int | None, + quantity: Quantity | None, + aux_items: Sequence[int | str] | None, *, - coords: XYZCoords, + coords: XYZCoords | NetworkCoords, ) -> xr.Dataset: - """Convert accepted input data to an xr.Dataset""" + """Convert accepted input data to an xr.Dataset.""" + + # name -> the name of the model result + # item -> the name of the element of interest in the data + # quantity -> the physical quantity that the variable of interest represents data, name = _open_and_name(data, name) sel_items = _select_items(data, item, aux_items) @@ -247,21 +257,36 @@ def _parse_point_input( varname = _select_variable_name(name, sel_items) ds = _convert_to_dataset(data, varname, sel_items) - ds = _include_attributes(ds, varname, quantity, sel_items) ds = _include_coords(ds, coords=coords) + ds = _include_attributes(ds, varname, quantity, sel_items) return ds def _parse_xyz_point_input( data: PointType, - name: Optional[str], + name: str | None, item: str | int | None, - quantity: Optional[Quantity], - x: Optional[float], - y: Optional[float], - z: Optional[float], - aux_items: Optional[Sequence[int | str]], + quantity: Quantity | None, + x: float | None, + y: float | None, + z: float | None, + aux_items: Sequence[int | str] | None, ) -> xr.Dataset: 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 | None, + aux_items: Sequence[int | str] | None, +) -> xr.Dataset: + if node is None: + raise ValueError("'node' argument cannot be empty.") + coords = NetworkCoords(node=node) + 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 52ff26ca4..6b6d02193 100644 --- a/src/modelskill/timeseries/_timeseries.py +++ b/src/modelskill/timeseries/_timeseries.py @@ -1,7 +1,8 @@ from __future__ import annotations from copy import deepcopy from dataclasses import dataclass -from typing import ClassVar, Literal, Optional, TypeVar, Any +from typing import ClassVar, Literal, TypeVar, Any +from typing_extensions import Self import warnings import numpy as np import pandas as pd @@ -63,13 +64,17 @@ def _validate_dataset(ds: xr.Dataset) -> xr.Dataset: ), "time must be increasing (please check for duplicate times))" # Validate coordinates - if "x" not in ds.coords: - raise ValueError("data must have an x-coordinate") - if "y" not in ds.coords: - raise ValueError("data must have a y-coordinate") - if "z" not in ds.coords: - ds.coords["z"] = np.nan - # assert "z" in ds.coords, "data must have a z-coordinate" + # Check for either traditional x,y coordinates OR node-based coordinates + has_spatial_coords = "x" in ds.coords and "y" in ds.coords + has_node_coord = "node" in ds.coords + + if not has_spatial_coords and not has_node_coord: + raise ValueError("data must have either x,y coordinates or a node coordinate") + + if has_spatial_coords: + # Traditional spatial data - ensure z coordinate exists + if "z" not in ds.coords: + ds.coords["z"] = np.nan # Validate data vars = [v for v in ds.data_vars] @@ -108,9 +113,10 @@ def _validate_dataset(ds: xr.Dataset) -> xr.Dataset: if ds.attrs["gtype"] not in [ str(GeometryType.POINT), str(GeometryType.TRACK), + str(GeometryType.NODE), ]: raise ValueError( - f"data attribute 'gtype' must be one of {GeometryType.POINT} or {GeometryType.TRACK}" + f"data attribute 'gtype' must be one of {GeometryType.POINT}, {GeometryType.TRACK}, or {GeometryType.NODE}" ) if "long_name" not in da.attrs: da.attrs["long_name"] = Quantity.undefined().name @@ -222,7 +228,14 @@ def y(self) -> Any: def y(self, value: Any) -> None: self.data["y"] = value - def _coordinate_values(self, coord: str) -> float | np.ndarray: + @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 @@ -248,7 +261,12 @@ def __repr__(self) -> str: res = [] res.append(f"<{self.__class__.__name__}>: {self.name}") if self.gtype == str(GeometryType.POINT): - res.append(f"Location: {self.x}, {self.y}") + # 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}") res.append(f"Time: {self.time[0]} - {self.time[-1]}") res.append(f"Quantity: {self.quantity}") if len(self._aux_vars) > 0: @@ -298,23 +316,32 @@ def to_dataframe(self) -> pd.DataFrame: if self.gtype == str(GeometryType.POINT): # we remove the scalar coordinate variables as they # will otherwise be columns in the dataframe - return self.data.drop_vars(["x", "y", "z"]).to_dataframe() + # Only drop coordinates that exist + coords_to_drop = [] + for coord in ["x", "y", "z"]: + if coord in self.data.coords: + coords_to_drop.append(coord) + return self.data.drop_vars(coords_to_drop).to_dataframe() elif self.gtype == str(GeometryType.TRACK): - df = self.data.drop_vars(["z"]).to_dataframe() - # make sure that x, y cols are first - cols = ["x", "y"] + [c for c in df.columns if c not in ["x", "y"]] + coords_to_drop = ["z"] if "z" in self.data.coords else [] + df = self.data.drop_vars(coords_to_drop).to_dataframe() + # makes sure that x comes first, then y, then other columns alphabetically + priority = {"x": 0, "y": 1} + cols = sorted(df.columns, key=lambda col: (priority.get(col, 999), col)) return df[cols] + elif self.gtype == str(GeometryType.NODE): + return self.data.drop_vars(["node"]).to_dataframe() else: raise NotImplementedError(f"Unknown gtype: {self.gtype}") def sel(self: T, **kwargs: Any) -> T: """Select data by label""" - return self.__class__(self.data.sel(**kwargs)) + return self._create_new_instance(self.data.sel(**kwargs)) def trim( self: T, - start_time: Optional[pd.Timestamp] = None, - end_time: Optional[pd.Timestamp] = None, + start_time: pd.Timestamp | None = None, + end_time: pd.Timestamp | None = None, buffer: str = "1s", no_overlap: Literal["ignore", "error", "warn"] = "error", ) -> T: @@ -347,4 +374,11 @@ def trim( case _: pass + return self._create_new_instance(data) + + def _create_new_instance(self, data: xr.Dataset) -> Self: + """Create a new instance of this class with the given data. + + Subclasses can override this to handle their specific constructor requirements. + """ return self.__class__(data) diff --git a/src/modelskill/timeseries/_track.py b/src/modelskill/timeseries/_track.py index c0779a10e..9dcee6dea 100644 --- a/src/modelskill/timeseries/_track.py +++ b/src/modelskill/timeseries/_track.py @@ -2,7 +2,7 @@ from collections.abc import Hashable from dataclasses import dataclass from pathlib import Path -from typing import Literal, get_args, Optional, List, Sequence +from typing import Literal, get_args, List, Sequence import warnings import pandas as pd import xarray as xr @@ -32,7 +32,7 @@ def _parse_track_items( x_item: int | str | None, y_item: int | str | None, item: int | str | None, - aux_items: Optional[Sequence[int | str]] = None, + aux_items: Sequence[int | str] | None = None, ) -> TrackItem: """If input has exactly 3 items we accept item=None""" if len(items) < 3: @@ -64,14 +64,14 @@ def _parse_track_items( def _parse_track_input( data: TrackType, - name: Optional[str], + name: str | None, item: str | int | None, - quantity: Optional[Quantity], + quantity: Quantity | None, x_item: str | int | None, y_item: str | int | None, keep_duplicates: Literal["first", "last", False] = "first", offset_duplicates: float = 0.001, - aux_items: Optional[Sequence[int | str]] = None, + aux_items: Sequence[int | str] | None = None, ) -> xr.Dataset: assert isinstance( data, get_args(TrackType) diff --git a/src/modelskill/types.py b/src/modelskill/types.py index 0ba3dcd63..4efae505c 100644 --- a/src/modelskill/types.py +++ b/src/modelskill/types.py @@ -1,6 +1,6 @@ from enum import Enum from pathlib import Path -from typing import Union, List, Optional +from typing import Union, List from dataclasses import dataclass import pandas as pd import xarray as xr @@ -14,6 +14,7 @@ class GeometryType(Enum): TRACK = "track" UNSTRUCTURED = "unstructured" GRID = "grid" + NODE = "node" def __str__(self) -> str: return self.name.lower() @@ -37,6 +38,8 @@ def from_string(s: str) -> "GeometryType": >>> GeometryType.from_string("grid") + >>> GeometryType.from_string("node") + """ try: @@ -93,5 +96,5 @@ def from_string(s: str) -> "GeometryType": class Period: """Period of data, defined by start and end time, can be open ended""" - start: Optional[pd.Timestamp] = None - end: Optional[pd.Timestamp] = None + start: pd.Timestamp | None = None + end: pd.Timestamp | None = None diff --git a/tests/test_match.py b/tests/test_match.py index 54b8d7a23..3babf3a3a 100644 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -7,6 +7,10 @@ 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 @@ -71,6 +75,102 @@ def mr3(): return ms.model_result(fn, item=0, name="SW_3") +# 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) + + +@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) + + +@pytest.fixture +def network_mr(network): + """NetworkModelResult fixture""" + return ms.NetworkModelResult(network, name="Network_Model") + + +@pytest.fixture +def node_obs1(network): + """NodeObservation for node '100'""" + node_id = network.find(node="100") + time = pd.date_range("2017-10-27", periods=18, freq="h") + # Add some noise to make it different from model + np.random.seed(123) + data = np.random.normal(1.4, 0.2, len(time)) + df = pd.DataFrame({"WaterLevel": data}, index=time) + return ms.NodeObservation(df, node=node_id, name="Station_A") + + +@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") + np.random.seed(456) + data = np.random.normal(1.6, 0.25, len(time)) + df = pd.DataFrame({"WaterLevel": data}, index=time) + return ms.NodeObservation(df, node=node_id, name="Station_B") + + +@pytest.fixture +def node_obs_invalid(network): + """NodeObservation for a node that doesn't exist in the network""" + time = pd.date_range("2017-10-27", periods=10, freq="h") + data = np.random.normal(1.5, 0.2, len(time)) + df = pd.DataFrame({"WaterLevel": data}, index=time) + return ms.NodeObservation(df, node=999, name="Node_999_Obs") + + +@pytest.fixture +def network_mr1(network): + """First NetworkModelResult fixture""" + return ms.NetworkModelResult(network, name="Network_1") + + +@pytest.fixture +def network_mr2(network2): + """Second NetworkModelResult fixture with offset data""" + return ms.NetworkModelResult(network2, name="Network_2") + + +@pytest.fixture +def node_obs_gaps(network): + """NodeObservation with time gaps""" + node_id = network.find(node="100") + time = pd.date_range("2017-10-27", periods=10, freq="2h") # Different frequency + data = np.random.normal(1.5, 0.2, len(time)) + df = pd.DataFrame({"WaterLevel": data}, index=time) + return ms.NodeObservation(df, node=node_id, name="Node_100_Gaps") + + +@pytest.fixture +def network_mr_gaps(network): + """NetworkModelResult for gap testing""" + return ms.NetworkModelResult(network, name="Network_Gaps") + + +@pytest.fixture +def point_obs_error(): + """PointObservation for error testing (should not work with NetworkModelResult)""" + df = pd.DataFrame( + {"WL": [1, 2, 3]}, index=pd.date_range("2017-10-27", periods=3, freq="h") + ) + return ms.PointObservation(df, x=0.0, y=0.0) + + def test_properties_after_match(o1, mr1): cmp = ms.match(o1, mr1) assert cmp.n_models == 1 @@ -522,7 +622,10 @@ def test_multiple_obs_not_allowed_with_non_spatial_modelresults(): assert "m2" in cmp.mod_names # but this is not allowed - with pytest.raises(ValueError, match="SpatialField type"): + with pytest.raises( + ValueError, + match="When matching multiple observations with multiple models, all models", + ): ms.match(obs=[o1, o2], mod=[m1, m2, m3]) @@ -608,3 +711,87 @@ def test_multiple_models_same_name(tmp_path: Path) -> None: with pytest.raises(ValueError, match="HKZN_local_2017_DutchCoast"): ms.match(obs, [mr1, mr2]) + + +def test_match_node_obs_with_network_model(node_obs1, network_mr): + cmp = ms.match(node_obs1, network_mr) + assert cmp is not None + assert cmp.n_points > 0 + assert "Network_Model" in cmp.mod_names + + assert cmp.n_models == 1 + assert cmp.n_points == 18 + assert cmp.name == "Station_A" + assert cmp.gtype == "node" + 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 + assert cc.n_observations == 2 + assert "Station_A" in cc + assert "Station_B" in cc + assert cc["Station_A"].n_points == 18 # Limited by shortest time overlap + assert cc["Station_B"].n_points == 15 + + +def test_match_node_obs_with_multiple_network_models( + node_obs1, network_mr1, network_mr2 +): + # Test matching one observation with multiple network models + cmp = ms.match(node_obs1, [network_mr1, network_mr2]) + assert cmp.n_models == 2 + assert cmp.mod_names == ["Network_1", "Network_2"] + + +def test_match_network_invalid_node_error(node_obs_invalid, network_mr): + with pytest.raises(ValueError, match="Node 999 not found"): + ms.match(node_obs_invalid, network_mr) + + +def test_skill_index(node_obs1, node_obs2, network_mr): + """Test that NetworkModelResult correctly extracts node data during matching""" + cmp = ms.match([node_obs1, node_obs2], network_mr) + # Test that we can get skill metrics + skill = cmp.skill() + assert "Station_A" in skill.index + assert "Station_B" in skill.index + + +def test_network_match_with_time_gaps(node_obs_gaps, network_mr_gaps): + """Test network matching with gaps in observation data""" + cmp = ms.match(node_obs_gaps, network_mr_gaps) + assert cmp.n_points > 0 # Should still match some points + + +def test_network_match_multi_obs_multi_model_comprehensive( + node_obs1, node_obs2, network_mr1, network_mr2 +): + """Test comprehensive multi-observation multi-model network matching""" + # Match multiple observations with multiple network models + cc = ms.match([node_obs1, node_obs2], [network_mr1, network_mr2]) + + assert cc.n_models == 2 + assert cc.n_observations == 2 + assert cc["Station_A"].n_models == 2 + assert cc["Station_B"].n_models == 2 + assert cc["Station_A"].mod_names == ["Network_1", "Network_2"] + assert cc["Station_B"].mod_names == ["Network_1", "Network_2"] + + +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 only supports NodeObservation" + ): + ms.match(point_obs_error, network_mr) + + +def test_match_nodeobs_with_other_result(node_obs1, mr1): + """Test matching attempt between a node observation and non-network model""" + with pytest.raises( + NotImplementedError, + match="Extraction from .* to is not implemented.", + ): + ms.match(node_obs1, mr1) diff --git a/tests/test_network.py b/tests/test_network.py new file mode 100644 index 000000000..90aec5583 --- /dev/null +++ b/tests/test_network.py @@ -0,0 +1,436 @@ +"""Test network models and observations""" + +# ruff: noqa: E402 +import pytest + +pytest.importorskip("networkx") + +import pandas as pd +import xarray as xr +import numpy as np +import modelskill as ms +from modelskill.model.network import ( + NetworkModelResult, + NodeModelResult, +) +from modelskill.network import ( + Network, + BasicNode, + BasicEdge, +) +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) + ] + edges = [ + BasicEdge(f"e{i}", nodes[i], nodes[i + 1], length=100.0) + for i in range(len(nodes) - 1) + ] + return Network(edges) + + +@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 + + +@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) + + +@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"]) + ] + edges = [BasicEdge("e1", nodes[0], nodes[1], length=100.0)] + return Network(edges) + + +@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 + + +@pytest.fixture +def sample_node_data(): + """Sample node observation data""" + time = pd.date_range("2010-01-01", periods=10, freq="h") + + # Create sample data with some variation + np.random.seed(42) + data = np.random.randn(len(time)) * 0.1 + 1.5 + + df = pd.DataFrame({"WaterLevel": data}, index=time) + + return df + + +@pytest.fixture +def sample_series(sample_node_data): + """Sample node observation data as series""" + return sample_node_data["WaterLevel"] + + +class TestNetworkModelResult: + """Test NetworkModelResult class""" + + def test_init_with_network(self, sample_network): + """Test initialization with a Network object""" + nmr = NetworkModelResult(sample_network) + + assert len(nmr.time) == 10 + assert isinstance(nmr.time, pd.DatetimeIndex) + assert len(nmr.nodes) == 3 + + def test_init_with_name(self, sample_network): + """Test initialization with explicit name""" + nmr = NetworkModelResult(sample_network, name="Test_Network") + assert nmr.name == "Test_Network" + + def test_init_with_item_selection(self, sample_network_multivars): + """Test initialization with specific item selection""" + nmr = NetworkModelResult( + sample_network_multivars, item="WaterLevel", name="Network_WL" + ) + + assert nmr.name == "Network_WL" + assert "WaterLevel" in nmr.data.data_vars + assert "Discharge" not in nmr.data.data_vars + + def test_init_fails_with_unsupported_type(self): + """Test that passing a non-Network object raises an error""" + with pytest.raises((TypeError, AttributeError)): + NetworkModelResult(xr.Dataset()) # type: ignore[arg-type] + + def test_repr(self, sample_network): + """Test string representation""" + nmr = NetworkModelResult(sample_network, name="Test_Network") + repr_str = repr(nmr) + + assert "NetworkModelResult" in repr_str + assert "Test_Network" in repr_str + + 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, node=node_id, name="Node_123") + + extracted = nmr.extract(obs) + + assert isinstance(extracted, NodeModelResult) + assert extracted.node == node_id + assert len(extracted.time) == 10 + + 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, node=999, name="Node_999") + + with pytest.raises(ValueError, match="Node 999 not found"): + nmr.extract(obs) + + def test_extract_wrong_observation_type(self, sample_network): + """Test extraction with wrong observation type""" + nmr = NetworkModelResult(sample_network) + + df = pd.DataFrame( + {"WL": [1, 2, 3]}, index=pd.date_range("2010-01-01", periods=3, freq="h") + ) + obs = ms.PointObservation(df, x=0.0, y=0.0) + + with pytest.raises( + TypeError, match="NetworkModelResult only supports NodeObservation" + ): + nmr.extract(obs) + + +class TestNodeObservation: + """Test NodeObservation class""" + + @pytest.fixture + def multi_data(self, sample_node_data): + """Multi-column DataFrame with 3 stations""" + return pd.DataFrame( + { + "station_0": sample_node_data["WaterLevel"], + "station_1": sample_node_data["WaterLevel"] + 0.1, + "station_2": sample_node_data["WaterLevel"] + 0.2, + } + ) + + def test_init_with_df(self, sample_node_data): + """Test initialization with pandas DataFrame""" + + obs = NodeObservation( + sample_node_data, node=123, name="Sensor_1", item="WaterLevel" + ) + + assert obs.node == 123 + assert obs.name == "Sensor_1" + assert len(obs.time) == 10 + assert isinstance(obs.time, pd.DatetimeIndex) + + def test_init_with_series(self, sample_series): + """Test initialization with pandas Series""" + obs = NodeObservation(sample_series, node=456, name="Node_456") + + assert obs.node == 456 + assert obs.name == "Node_456" + assert len(obs.time) == 10 + + def test_node_attrs(self, sample_node_data): + """Test attrs property""" + attrs = {"source": "test", "version": "1.0"} + obs = NodeObservation(sample_node_data, node=123, attrs=attrs, weight=2.5) + + assert obs.attrs["source"] == "test" + assert obs.attrs["version"] == "1.0" + assert obs.weight == 2.5 + assert obs.quantity == Quantity.undefined() + + 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"}, + ) + + assert len(obs_list) == 3 + assert all(isinstance(obs, NodeObservation) for obs in obs_list) + + 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"}, + ) + + 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"}, + ) + + assert obs_list[0].name == "station_0" + assert obs_list[1].name == "station_1" + assert obs_list[2].name == "station_2" + + def test_from_xarray_dataset(self, sample_node_data): + ds = xr.Dataset( + { + "station_0": ("time", sample_node_data["WaterLevel"].values), + "station_1": ("time", sample_node_data["WaterLevel"].values + 0.1), + }, + coords={"time": sample_node_data.index}, + ) + obs_list = NodeObservation.from_multiple( + 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 + + def test_nodes_must_be_dict(self, multi_data): + with pytest.raises(TypeError, match="'nodes' must be a dict"): + NodeObservation.from_multiple(data=multi_data, nodes=123) + + 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"}, + attrs=attrs, + ) + + for obs in obs_list: + assert obs.attrs["source"] == "sensor_array" + assert obs.attrs["version"] == 2 + + def test_init_from_csv(self): + obs = NodeObservation( + "tests/testdata/network_sensor_1.csv", node=1, item="water_level@sens1" + ) + + assert obs.node == 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", + } + ) + + 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 + 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"} + ) + + assert len(obs_list) == 2 + 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" + + def test_nodes_none_raises(self, multi_data): + with pytest.raises(ValueError, match="'nodes' argument is required"): + NodeObservation.from_multiple(data=multi_data, nodes=None) + + def test_single_node_dict(self, sample_node_data): + obs_list = NodeObservation.from_multiple( + data=sample_node_data, nodes={123: "WaterLevel"} + ) + + assert len(obs_list) == 1 + assert isinstance(obs_list[0], NodeObservation) + assert obs_list[0].node == 123 + + +class TestNodeModelResult: + """Test NodeModelResult class""" + + @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") + + assert nmr.node == 123 + assert nmr.name == "Node_123_Model" + assert len(nmr.time) == 10 + + +class TestNetworkIntegration: + """Test integration between network models and observations""" + + 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, node=node_id, name="Node_123_Obs") + + extracted = nmr.extract(obs) + + assert isinstance(extracted, NodeModelResult) + assert extracted.node == node_id + assert extracted.name == "Network_Model" + assert len(extracted.time) == len(obs.time) + + 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, node=node_id, name="Node_123_Obs") + + comparer = ms.match(obs, nmr) + + assert comparer is not None + assert "Network_Model" in comparer.mod_names + assert comparer.n_points > 0 + + 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") + + 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, + } + ) + + node_0 = sample_network.find(node="123") + node_1 = sample_network.find(node="456") + node_2 = sample_network.find(node="789") + + # 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"}, + ) + + # Test that matching works + comparer_collection = ms.match(obs_list, nmr) + + assert comparer_collection is not None + assert len(comparer_collection) == 3 + + for comparer in comparer_collection: + assert "Network_Model" in comparer.mod_names + assert comparer.n_points > 0 diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index d362a2943..f2510e862 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -80,12 +80,12 @@ def test_timeseries_validation_fails_kind(ds_point): def test_timeseries_validation_fails_xy(ds_point): ds_without_x = ds_point.drop_vars("x") - with pytest.raises(ValueError, match="data must have an x-coordinate"): + with pytest.raises(ValueError, match="data must have either x,y"): TimeSeries(ds_without_x) # ds_point.coords["x"] = 0 ds_without_y = ds_point.drop_vars("y") - with pytest.raises(ValueError, match="data must have a y-coordinate"): + with pytest.raises(ValueError, match="data must have either x,y"): TimeSeries(ds_without_y) diff --git a/tests/testdata/network.nc b/tests/testdata/network.nc new file mode 100644 index 000000000..951c38b25 Binary files /dev/null and b/tests/testdata/network.nc differ diff --git a/tests/testdata/network.res1d b/tests/testdata/network.res1d new file mode 100644 index 000000000..45a661f6e Binary files /dev/null and b/tests/testdata/network.res1d differ diff --git a/tests/testdata/network_sensor_1.csv b/tests/testdata/network_sensor_1.csv new file mode 100644 index 000000000..904d9eb79 --- /dev/null +++ b/tests/testdata/network_sensor_1.csv @@ -0,0 +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 diff --git a/tests/testdata/network_sensor_2.csv b/tests/testdata/network_sensor_2.csv new file mode 100644 index 000000000..9eddb84df --- /dev/null +++ b/tests/testdata/network_sensor_2.csv @@ -0,0 +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 diff --git a/tests/testdata/network_sensor_3.csv b/tests/testdata/network_sensor_3.csv new file mode 100644 index 000000000..540162613 --- /dev/null +++ b/tests/testdata/network_sensor_3.csv @@ -0,0 +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