17  Reading res1d

This section guides you through loading network results (e.g., from res1d files) into Pandas DataFrames. The approach is similar to that used with MIKE IO, allowing you to apply your DataFrame knowledge to network results.

17.1 Workflow

The general workflow for network results in MIKE IO 1D is as follows:

  1. Open the network result file to obtain a Res1D object.
  2. Optionally, subset this Res1D object to select specific locations, quantities, or time steps.
  3. Convert the (subsetted) data to a DataFrame.
  4. Perform some additional analysis via the DataFrame.
Tip

This workflow is quite similar to the one you learned in Module 2 for handling dfs0 files with MIKE IO. The core idea of opening a file, accessing data, and then often converting to a DataFrame remains consistent.

17.2 Res1D

The Res1D object is central to interacting with 1D network result files. It acts as the primary container for your simulation data, analogous to MIKE IO’s Dataset object for other DHI file types.

To start, you open your result file using mikeio1d.open():

import mikeio1d

res = mikeio1d.open("data/network.res1d")
res
<mikeio1d.Res1D>
Note

MIKE IO uses read() to create a Dataset, whereas MIKE IO 1D uses open() to create a Res1D object.

Get an overview of key meta data with info().

res.info()
Start time: 1994-08-07 16:35:00
End time: 1994-08-07 18:35:00
# Timesteps: 110
# Catchments: 0
# Nodes: 119
# Reaches: 118
# Globals: 0
0 - Water level (m)
1 - Discharge (m^3/s)

Notice the this produces similar information to that of a MIKE IO Dataset:

  • Total number of time steps
  • Timestamps for first and last time step
  • All available quantities

Like dfs0 files, all results within a Res1D object share a common time axis.

17.3 Convert to DataFrame

The read() method is the primary way to convert data from a Res1D object (or its subsets) into a Pandas DataFrame. This is a versatile method that can be called at various levels.

You can convert the entire content of the Res1D object into a single DataFrame. Each quantity at a specific location becomes a column in the DataFrame.

df = res.read()
df.head()
WaterLevel:1 WaterLevel:2 WaterLevel:3 WaterLevel:4 WaterLevel:5 WaterLevel:6 WaterLevel:7 WaterLevel:8 WaterLevel:9 WaterLevel:10 ... Discharge:99l1:22.2508 WaterLevel:9l1:0 WaterLevel:9l1:10 Discharge:9l1:5 WaterLevel:Weir:119w1:0 WaterLevel:Weir:119w1:1 Discharge:Weir:119w1:0.5 WaterLevel:Pump:115p1:0 WaterLevel:Pump:115p1:82.4281 Discharge:Pump:115p1:41.214
1994-08-07 16:35:00.000 195.052994 195.821503 195.8815 193.604996 193.615005 193.625000 193.675003 193.764999 193.774994 193.804993 ... 0.000002 193.774994 193.764999 0.000031 193.550003 188.479996 0.0 193.304993 195.005005 0.0
1994-08-07 16:36:01.870 195.052994 195.821701 195.8815 193.604996 193.615005 193.625320 193.675110 193.765060 193.775116 193.804993 ... 0.000002 193.775070 193.765060 0.000031 193.550003 188.479996 0.0 193.306061 195.005005 0.0
1994-08-07 16:37:07.560 195.052994 195.821640 195.8815 193.604996 193.615005 193.625671 193.675369 193.765106 193.775513 193.804993 ... 0.000002 193.775391 193.765106 0.000033 193.550034 188.479996 0.0 193.307144 195.005005 0.0
1994-08-07 16:38:55.828 195.052994 195.821503 195.8815 193.604996 193.615005 193.626236 193.675751 193.765228 193.776077 193.804993 ... 0.000002 193.775894 193.765228 0.000037 193.550079 188.479996 0.0 193.308884 195.005005 0.0
1994-08-07 16:39:55.828 195.052994 195.821503 195.8815 193.604996 193.615005 193.626556 193.675949 193.765335 193.776352 193.804993 ... 0.000002 193.776154 193.765335 0.000039 193.550095 188.479996 0.0 193.309860 195.005005 0.0

5 rows × 495 columns

The DataFrame above includes all reaches and nodes, which results in many columns. To create more manageable DataFrames, call read() on data subsets (e.g., by location or quantity).

For example, all results for the location group reaches:

df_reaches = res.reaches.read()
df_reaches.head()
WaterLevel:100l1:0 WaterLevel:100l1:47.6827 WaterLevel:101l1:0 WaterLevel:101l1:66.4361 WaterLevel:102l1:0 WaterLevel:102l1:10.9366 WaterLevel:103l1:0 WaterLevel:103l1:26.0653 WaterLevel:104l1:0 WaterLevel:104l1:34.4131 ... Discharge:93l1:24.5832 Discharge:94l1:21.2852 Discharge:95l1:21.9487 Discharge:96l1:14.9257 Discharge:97l1:5.71207 Discharge:98l1:8.00489 Discharge:99l1:22.2508 Discharge:9l1:5 Discharge:Weir:119w1:0.5 Discharge:Pump:115p1:41.214
1994-08-07 16:35:00.000 195.441498 194.661499 195.931503 195.441498 193.550003 193.550003 195.801498 195.701508 197.072006 196.962006 ... 0.000004 0.000003 0.000001 0.000005 0.000013 0.000003 0.000002 0.000031 0.0 0.0
1994-08-07 16:36:01.870 195.441498 194.661621 195.931503 195.441605 193.550140 193.550064 195.801498 195.703171 197.072006 196.962051 ... 0.000004 0.000003 0.000001 0.000005 0.000010 0.000003 0.000002 0.000031 0.0 0.0
1994-08-07 16:37:07.560 195.441498 194.661728 195.931503 195.441620 193.550232 193.550156 195.801498 195.703400 197.072006 196.962082 ... 0.000004 0.000003 0.000001 0.000005 0.000010 0.000003 0.000002 0.000033 0.0 0.0
1994-08-07 16:38:55.828 195.441498 194.661804 195.931503 195.441605 193.550369 193.550308 195.801498 195.703690 197.072006 196.962112 ... 0.000004 0.000003 0.000001 0.000005 0.000009 0.000003 0.000002 0.000037 0.0 0.0
1994-08-07 16:39:55.828 195.441498 194.661972 195.931503 195.441605 193.550430 193.550369 195.801498 195.703827 197.072006 196.962128 ... 0.000004 0.000003 0.000001 0.000005 0.000009 0.000003 0.000002 0.000039 0.0 0.0

5 rows × 376 columns

Or results for a reach named 100l1:

df_reach_100l1 = res.reaches["100l1"].read()
df_reach_100l1.head()
WaterLevel:100l1:0 WaterLevel:100l1:47.6827 Discharge:100l1:23.8414
1994-08-07 16:35:00.000 195.441498 194.661499 0.000006
1994-08-07 16:36:01.870 195.441498 194.661621 0.000006
1994-08-07 16:37:07.560 195.441498 194.661728 0.000006
1994-08-07 16:38:55.828 195.441498 194.661804 0.000006
1994-08-07 16:39:55.828 195.441498 194.661972 0.000006

Or just the Discharge quantity of the previous reach.

df_reach_100l1_q = res.reaches["100l1"].Discharge.read()
df_reach_100l1_q.head()
Discharge:100l1:23.8414
1994-08-07 16:35:00.000 0.000006
1994-08-07 16:36:01.870 0.000006
1994-08-07 16:37:07.560 0.000006
1994-08-07 16:38:55.828 0.000006
1994-08-07 16:39:55.828 0.000006

In the coming sections, we will cover how to explore the network structure of Res1D and select data. For now, just know that it’s possible to call read() from various sub-objects of Res1D to obtain a DataFrame.

Tip

Once you have your network data in a Pandas DataFrame using .read(), you can apply all the powerful analysis, manipulation, and visualization techniques you learned in Module 1 and Module 2.

17.4 Plotting and other formats

For convenience, a plot() method is available anywhere that read() can be called. This allows for quick visualization of the data.

res.reaches["100l1"].Discharge.plot()

Similarly, wherever read() is available, you can export data to other common formats. For example:

res.reaches["100l1"].Discharge.to_dfs0("discharge_of_interest.dfs0")
res.reaches["100l1"].Discharge.to_csv("discharge_of_interest.csv")

17.5 Practical Example

Let’s combine some of these concepts to accomplish the following objectives:

  1. Get descriptive statistics of all node water levels
  2. Get descriptive statistics of all reach discharges

First, open the result file.

import mikeio1d

res = mikeio1d.open("data/network.res1d")

Then, get our node water levels and reach discharges into DataFrames.

df_nodes_wl = res.nodes.WaterLevel.read()
df_reaches_q = res.reaches.Discharge.read()

Use describe() on each DataFrame, just like from previous modules.

wl_stats = df_nodes_wl.describe().T
wl_stats.head()
count mean std min 25% 50% 75% max
WaterLevel:1 110.0 195.198898 0.159523 195.052994 195.090881 195.131989 195.258179 195.669006
WaterLevel:2 110.0 195.822342 0.000657 195.821503 195.821503 195.822784 195.822937 195.822968
WaterLevel:3 110.0 195.881470 0.000000 195.881500 195.881500 195.881500 195.881500 195.881500
WaterLevel:4 110.0 193.947418 0.348278 193.604996 193.614719 193.891739 194.173325 194.661331
WaterLevel:5 110.0 194.010544 0.379473 193.615005 193.659035 193.940742 194.260757 194.793060
q_stats = df_reaches_q.describe().T
q_stats.head()
count mean std min 25% 50% 75% max
Discharge:100l1:23.8414 110.0 0.014078 0.026875 0.000006 0.000636 0.001032 0.005988 0.099751
Discharge:101l1:33.218 110.0 -0.000034 0.005649 -0.022655 0.000004 0.000005 0.000022 0.019202
Discharge:102l1:5.46832 110.0 0.069058 0.100331 -0.011316 0.003062 0.017987 0.096062 0.326383
Discharge:103l1:13.0327 110.0 -0.000011 0.001084 -0.006748 0.000002 0.000017 0.000337 0.001056
Discharge:104l1:17.2065 110.0 0.000005 0.000002 0.000003 0.000005 0.000005 0.000005 0.000025

From here we might want to use existing Pandas functionality to export our data for reporting purposes:

wl_stats.to_excel("node_water_levels.xlsx")
q_stats.to_excel("reaches_discharges.xlsx")
Tip

You need to install a Python package like openpyxl to use Pandas’ to_excel() method.