DHI/mikeio1d

Version 0.4.0 can't do df.read(mikeio1d.res1d.QueryDataReach("Discharge", 'tunnel_name'))

Havrevoll opened this issue · 6 comments

I have a res1d file which contains the results from a simulation in Mike+. I imported that file with
df = mikeio1d.Res1D("model_m1d - Result Files/modelBaseDefault_Network_HD.res1d")
One of the reaches in the model is a tunnel which I earlier opened with
discharge = df.read(mikeio1d.res1d.QueryDataReach("Discharge", 'Fictious outlet Valldalen-Basin'))
But I get an error:
NoDataForQuery: Invalid query Discharge:Fictious outlet Valldalen-Basin

It works properly in mikeio1d version 0.3.0, but not in 0.4.0. Is it a bug?

Hi!

It looks like this happens since the chainage is not included in QueryDataReach. In version 0.3.0, if the chainage wasn't specified, then it automatically used the start chainage. So, the quick fix would be to add the chainage as a third argument to QueryDataReach.

In version 0.4.0, there's a shorter way to write this:

from mikeio1d import Res1D
res = Res1D("model_m1d - Result Files/modelBaseDefault_Network_HD.res1d")
df = res.result_network.reaches.Fictious_outlet_Valldalen_Basin.Discharge.read()

That will return a pandas dataframe with discharge at all of the chainages along the reach.

Thank you for the answer! That was very helpful. Is this syntax documented anywhere?

How would I do if I wanted the discharge from several reaches into one dataframe? Earlier, I would then write res.read([mikeio1d.res1d.QueryDataReach("Discharge", a) for a in tunnels])
since the discharge is the same in all chainages of every reach, it was unimportant to specify chainage. Is there a way I can just get the start or end chainage without knowing it? Or to get a list of the first chainages of every reach in a list?

Thank you for the answer! That was very helpful. Is this syntax documented anywhere?

Good to hear. The syntax is included in some of the example notebooks for now. We need to update the overall documentation, so that'll come in future releases to make it easier :)

How would I do if I wanted the discharge from several reaches into one dataframe? Earlier, I would then write res.read([mikeio1d.res1d.QueryDataReach("Discharge", a) for a in tunnels]) since the discharge is the same in all chainages of every reach, it was unimportant to specify chainage. Is there a way I can just get the start or end chainage without knowing it? Or to get a list of the first chainages of every reach in a list?

It would be interesting to see how you created 'tunnels' here.

For a temporary fix, you could revert to the old behavior with:

res = Res1D('myfile.res1d', result_reader_type='query')
# continue with the code you had

With 0.4.0 there's a few options, but I think it could probably be improved here.

Option 1 - use autocompletion to find chainage names

from mikeio1d import Res1D
res = Res1D("results.res1d")
# here there's autocompletion that will show reach names, as well as chainages with 'm_' prefix
res.result_network.reaches.Some_Reach_Name.m_1000.Discharge.read()

Option 2 - directly access the first discharge gridpoint

from mikeio1d import Res1D
res = Res1D("model_m1d - Result Files/modelBaseDefault_Network_HD.res1d")
df = res.result_network.reaches.Fictious_outlet_Valldalen_Basin.Discharge.result_quantities[0].read()

For reading multiple reaches into a single dataframe, you can use several add() followed by read()

from mikeio1d import Res1D
res = Res1D("model_m1d - Result Files/modelBaseDefault_Network_HD.res1d")
# here you add quantities to a background 'queue'
res.result_network.reaches.Fictious_outlet_Valldalen_Basin.Discharge.result_quantities[0].add()
res.result_network.reaches.AnotherReach.Discharge.result_quantities[0].add()
# here you read everything in the queue
df = res.read()

Would a syntax like this be useful for your script? This could be an idea for 0.5.0

from mikeio1d import Res1D
res = Res1D('result.res1d')

# select discharges to be read
for reach in res.network.reaches:
    if 'tunnel' in reach.name:
        reach.Discharge[0].add()    # default grabs all chainages, or zero-based index to grab first

# read discharges into pandas dataframe
df = res.read()

Thanks for the tips. I used option 2, with the add() and read() methods. My tunnels list looks like this:
tunnels = ['Fictious outlet Valldalen-Basin', 'Inntaksluke-Risbu', 'Fictious outlet Vasstøl', 'Vasstøl-Øyna']
I manually copied the names from the output of res.reaches.keys(). Here, the list was short enough that I could just translate the names to "snake_case" and skip the for-loop, but this would get clumsy if the list was very long. So yes, your suggested syntax would be welcome in the future.

For me it is not so intuitive to use node or reach names as attributes (is that the correct term?), and I can imagine some problems in translating from one to the other, i.e. a list of strings to a list of attributes, if that is even possible. For me it would be more practical to be able to write res.result_network.reaches['Fictious outlet Valldalen-Basin'].Discharge.result_quantities[0].read(). Is there a reason why I can't do that instead of your suggestion for v.0.5.0?