Module hydroinform.Mike_river

Source code
# -*- coding: utf-8 -*-
from hydroinform import DFS
import pandas as pd
import numpy as np

class Point(object):
    def __init__(self, x=0.0,y=0.0):
        self.x=x
        self.y=y

class RiverPoint(Point):
    """
    A point in a river with a chainage
    """
    def __init__(self, x = 0, y = 0, chainage=0.0):
        super(RiverPoint, self).__init__(x, y)
        self.chainage = chainage

class GridPoint(RiverPoint):
    """
    A Gridpoint in a river
    """
    def __init__(self, x=0,y=0,chainage=0, z=0.0, type=1):
        super(GridPoint, self).__init__(x,y,chainage)
        self.z=z
        self.type=type
        self.index = -1
        self.itemindex = -1
        self.point_type=''
        self.name=''
        self.downstream=''


class Node(Point):
    """description of class"""
    def __init__(self, x=0,y=0,Type=1,name=''):
        super(Node, self).__init__(x,y)
        self.type =Type
        self.name=name

class Reach(object):

    def __init__(self, type, name, topoid, flowdir):
        self.type = type
        self.id=-1
        self.name = name
        self.topo_id = topoid
        self.flow_direction = flowdir
        self.upstream_node=None
        self.downstream_node =None
        self.digi_points=[]
        self.grid_points=[]
        self.grid_points_h=[]
        self.grid_points_q=[]
        self.xsecs =[]
        self.dataitems={}
    

class xsecpoint(object):
    """
    A point in a cross section. Has x and z
    """
    def __init__(self, x, z):
        self.x=x
        self.z=z
        self.marker=-1

class xsec(object):
    """
    A Cross section
    """
    def __init__(self, name, NumberOfPoints):
        self.name =name
        self.number_of_points = NumberOfPoints
        self.xsec_points=[]
        self.type =-1

class DataItem(object):
    def __init__(self, name):
        self.name = name
        self.chainages =[]
        self.offset=0
        return super(DataItem, self).__init__()

class River_setup(object):
    """
    A river setup consisting of nodes and reaches
    """
    def __init__(self):
        self.nodes=[]
        self.reaches=[]
        return super(River_setup, self).__init__()

    @classmethod
    def from_file(cls, file_name):
        """
        Creates a river setup from either a .res1D-file or a .res11-file
        """
        import os
        from pathlib import Path
        if not os.path.isfile(file_name):
            raise FileNotFoundError(file_name)
        ext=Path(file_name).suffix.lower()
        cls = River_setup()
        if(ext == '.res1d'):
            cls.read_res1d(file_name)
            cls.make_unique_point_names()
        elif(ext == '.res11'):
            cls.read_res11(file_name)
        return cls;


    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.dispose()

    def dispose(self):
        self.res.dispose()

    def get_values(self, Item=1, TimeStep=0):
        """
        Returns the values for a particular time step and item in a double dictionary with first branch name and then chainage
        """
        toreturn={}
        for reach in self.reaches:
            if Item-1 in reach.dataitems:
                values = self.res.get_data(TimeStep,Item)[reach.dataitems[Item-1].offset: reach.dataitems[Item-1].offset + len(reach.dataitems[Item-1].chainages)]
                for i in range(0, len (values)):
                    if reach.name not in toreturn:
                        toreturn[reach.name] ={}
                    toreturn[reach.name][reach.dataitems[Item-1].chainages[i]]=values[i]
        for reach, val in toreturn.items():
            toreturn[reach]= sorted(val.items(), key=lambda x: x[0])
        return toreturn


    def read_res1d(self, Filename):
        """
        Reads a res1D file and builds the network
        """
        self.res= DFS.RES.from_file(Filename)
        nodestart=1

        NumberOfDynamicItems = self.res.StaticItems[nodestart].read_data()[0]
        self.DataItems =[]

        names = self.res.StaticItems[nodestart+1].read_data()
        names=names.split(';')
        for i in range(0, NumberOfDynamicItems):
            self.DataItems.append(DataItem(names[i].replace("\"", "")))
            self.DataItems[i].grouptype =self.res.StaticItems[nodestart+2].read_data()[i]
            self.DataItems[i].ItemIndex=i

        listlength = self.res.StaticItems[nodestart+5].read_data()
        nodestart+=6
        for i in range(0, NumberOfDynamicItems):
            if listlength[i]!=0:
                self.DataItems[i].ReachIndeces=self.res.StaticItems[nodestart].read_data()
                nodestart+=1

        for i in range(0, NumberOfDynamicItems):
            if self.DataItems[i].grouptype==2:
                self.DataItems[i].ReachNoVals=self.res.StaticItems[nodestart].read_data()
                self.DataItems[i].chainages=self.res.StaticItems[nodestart+1].read_data()
                nodestart+=2

        #Read nodes
        while self.res.StaticItems[nodestart].name!='NoNodes':
            nodestart+=1

        numberofnodes = self.res.StaticItems[nodestart].read_data()[0]
        xs =self.res.StaticItems[nodestart+3].read_data()
        ys =self.res.StaticItems[nodestart+4].read_data()
        types =self.res.StaticItems[nodestart+1].read_data()
        names = self.res.StaticItems[nodestart+2].read_data().split(';')
        for i in range(0,numberofnodes):
            self.nodes.append(Node(xs[i],ys[i],types[i], names[i]))

        #Read reaches
        reachstart= nodestart + 5
        numberofreaches = self.res.StaticItems[reachstart].read_data()[0]
        reachtypes = self.res.StaticItems[reachstart+1].read_data();
        allnames= self.res.StaticItems[reachstart+2].read_data()
        reachnames = allnames.split(';')
        topoids = self.res.StaticItems[reachstart+3].read_data().split(';')
        ups = self.res.StaticItems[reachstart+4].read_data()
        dws = self.res.StaticItems[reachstart+5].read_data()
        dir = self.res.StaticItems[reachstart+6].read_data()
        nodigipoints = self.res.StaticItems[reachstart+7].read_data()
        nogridpoints = self.res.StaticItems[reachstart+8].read_data()
        for i in range(0,numberofreaches):
            r = Reach(reachtypes[i], reachnames[i].replace("\"", ""), topoids[i].replace("\"", ""), dir[i])
            r.upstream_node = self.nodes[ups[i]]
            r.downstream_node = self.nodes[dws[i]]
            r.number_of_digi_points=nodigipoints[i]
            r.number_of_grid_points=nogridpoints[i]
            self.reaches.append(r)

        for di in self.DataItems:
            if di.grouptype==2:
                if not hasattr(di, 'ReachIndeces'):
                    di.ReachIndeces=range(0,numberofreaches)
                lcount=0
                for i in di.ReachIndeces:
                    ldi =DataItem(di.name)
                    ldi.ItemIndex =di.ItemIndex
                    ldi.offset =di.offset
                    ldi.chainages = di.chainages[di.offset:di.offset+di.ReachNoVals[lcount]]
                    di.offset +=di.ReachNoVals[lcount]
                    lcount+=1
                    self.reaches[i].dataitems[di.ItemIndex]=ldi

        qitem = next(i for i in self.DataItems if i.name =='Discharge')
        hitem = next(i for i in self.DataItems if i.name =='Water level' and i.grouptype==2)

        #Now build each individual reach
        Hindex=0
        Qindex=0
        rcount=0
        offset = reachstart + 9
        for r in self.reaches:
            if self.res.StaticItems[offset].name!='dpChainages':
                k=1

            #The digipoints
            dpChainages = self.res.StaticItems[offset].read_data()
            dpXs = self.res.StaticItems[offset+1].read_data()
            dpYs = self.res.StaticItems[offset+2].read_data()
            for i in range(0, r.number_of_digi_points):
                dp = RiverPoint(dpXs[i],dpYs[i], dpChainages[i])
                r.digi_points.append(dp)

            #The gridpoints
            gpType = self.res.StaticItems[offset+3].read_data()
            gpChainages = self.res.StaticItems[offset+4].read_data()
            gpXs = self.res.StaticItems[offset+5].read_data()
            gpYs = self.res.StaticItems[offset+6].read_data()
            gpZs = self.res.StaticItems[offset+7].read_data()
            for i in range(0, r.number_of_grid_points):
                dg = GridPoint(gpXs[i],gpYs[i], gpChainages[i], gpZs[i], gpType[i])
                dg.name = r.topo_id +"_" + r.name+"_" + str(dg.chainage)
                r.grid_points.append(dg)

            for dataitem in r.dataitems.values():
                for ci in range(len(dataitem.chainages)):
                    dg = next(dgp for dgp in r.grid_points if np.float32(dgp.chainage) == dataitem.chainages[ci])

                    if dataitem.name == "Water level":
                        dg.Hitemindex = dataitem.ItemIndex
                        dg.Hindex = ci + dataitem.offset;
                        r.grid_points_h.append(dg)
                        dg.point_type+='H'
                    elif dataitem.name == "Discharge":
                        dg.itemindex = dataitem.ItemIndex
                        dg.index = ci + dataitem.offset;
                        r.grid_points_q.append(dg)
                        dg.point_type+='Q'

          
                 
            #Some structures
            if self.res.StaticItems[offset+8].name=='structureNumberOfSubTypes':
                offset +=3
            
            #We may already be at the next reach
            if self.res.StaticItems[offset+10].name=='dpChainages':
                offset+=10
            elif self.res.StaticItems[offset+8].name=='dpChainages':
                offset+=8
            else: #Now for xsecs
                csIDs = self.res.StaticItems[offset+8].read_data().split(';')
                csnopoints= self.res.StaticItems[offset+10].read_data()

                cstypes= self.res.StaticItems[offset+9].read_data()
                csXs = self.res.StaticItems[offset+11].read_data()
                csZs = self.res.StaticItems[offset+12].read_data()
                csNoMarkers = self.res.StaticItems[offset+13].read_data()
                csMarkers = self.res.StaticItems[offset+14].read_data()
                csMarkersIndices = self.res.StaticItems[offset+15].read_data()
                csstart=0
                csmarkstart =0
                gpoffset=0
                for i in range(0, len(csnopoints)):
                    if cstypes[i+gpoffset] == 1000:
                        gpoffset+=1
                    cs = xsec(csIDs[i+gpoffset].replace("\"", ""), csnopoints[i])
                    cs.type = cstypes[i]
                    for n in range(csstart, csstart+ cs.number_of_points):
                        cs.xsec_points.append(xsecpoint(csXs[n],csZs[n]))
                    r.xsecs.append(cs);
                    csstart+=cs.number_of_points
                    for k in range(csmarkstart, csmarkstart + csNoMarkers[i]):
                        cs.xsec_points[csMarkersIndices[k]].marker=csMarkers[k]
                    csmarkstart += csNoMarkers[i]
                    cs.Gridpoint = [gp for gp in r.grid_points if gp.type==1025][i+gpoffset]

                offset+=16


    def make_unique_point_names(self):
        """
        Creates unique point names by adding "_Downstream" to the point with the highest flow of two duplicate gridpoints
        This is necessary because MikeHydro has duplicate points (TopoId, Branch, chainage) for at least all reach conversions
        """
        from functools import reduce
        point_dict={}
        for p in reduce(list.__add__,[r.grid_points_q for r in self.reaches]):
            if p.name in point_dict:
                if self.get_timeseries_Q(point_dict[p.name]).mean()>=self.get_timeseries_Q(p).mean():
                    pup = point_dict[p.name]
                    pup.name = p.name + '_Downstream';
                    pup.downstream='Downstream'
                    point_dict[pup.name]=pup;
                else:
                    p.downstream='Downstream'
                    p.name = p.name + '_Downstream';
            point_dict[p.name]=p;


    def read_res11(self, Filename):
        """
        Reads a res11 file and builds the network
        """
        self.res= DFS.RES.from_file(Filename)
        nodestart=0

        NumberOfstaticItemsPrBranch = self.res.StaticItems[0].read_data()[0]
        NumberOfBranches = self.res.StaticItems[1].read_data()[0]
        self.DataItems =[]

        nitems = len(self.res.items);

        nodestart =4
        while len(self.reaches)<NumberOfBranches:
            name = self.res.StaticItems[nodestart].read_data();
            topoid = self.res.StaticItems[nodestart+1].read_data();
            r = Reach(1, name, topoid, 1)
            self.reaches.append(r)
            gpChainages =self.res.StaticItems[nodestart+2].read_data()
            r.number_of_grid_points = len(gpChainages)
            gpXs =self.res.StaticItems[nodestart+3].read_data()
            gpYs =self.res.StaticItems[nodestart+4].read_data()
            gpZs =self.res.StaticItems[nodestart+5].read_data()
            gpRightBanks =self.res.StaticItems[nodestart+6].read_data()
            gpLeftBanks =self.res.StaticItems[nodestart+7].read_data()
            gptypes = self.res.StaticItems[nodestart+8].read_data()

            dpChainages =self.res.StaticItems[nodestart+9].read_data()
            r.number_of_digi_points = len(dpChainages)
            dpXs =self.res.StaticItems[nodestart+10].read_data()
            dpYs =self.res.StaticItems[nodestart+11].read_data()


            gpNumberOfRawdata = self.res.StaticItems[nodestart+13].read_data()
            connections = self.res.StaticItems[nodestart+12].read_data()
            # crosssections
            nodestart+=14
            while self.res.StaticItems[nodestart].name =='Cross section ID':
                crosssectionid = self.res.StaticItems[nodestart].read_data()
                rawXs = self.res.StaticItems[nodestart+1].read_data()
                rawZs = self.res.StaticItems[nodestart+2].read_data()
                nodestart+=4
            nodestart+=9

            #now build the reach
            Hindex=0
            Qindex=0
            for i in range(0, r.number_of_grid_points):
                dg = GridPoint(gpXs[i],gpYs[i], gpChainages[i], gpZs[i], gptypes[i])
                r.grid_points.append(dg)
                if dg.type==2:
                    r.grid_points_q.append(dg)
                    dg.index=Qindex
                    dg.itemindex = len(self.reaches) + int(nitems/2)
                    dg.point_type='Q'
                    Qindex+=1
                else:
                    r.grid_points_h.append(dg)
                    dg.Hindex = Hindex
                    dg.Hitemindex = len(self.reaches)
                    dg.point_type='H'
                    Hindex+=1

            for i in range(0, r.number_of_digi_points):
                dp = RiverPoint(dpXs[i],dpYs[i], dpChainages[i])
                r.digi_points.append(dp)

    def get_timeseries_Q(self, grid_point):
        """
        Returns a timeseries with flow data for a single grid point
        """
        if ( 'Q' in grid_point.point_type):
            #We need to add 1 because we have the item index not the item number
            return self.res.get_timeseries(grid_point.itemindex+1, grid_point.index)

    def get_timeseries_H(self, grid_point):
        """
        Returns a timeseries with water level data for a single grid point
        """
        if ( 'H' in grid_point.point_type):
            #We need to add 1 because we have the item index not the item number
            return self.res.get_timeseries(grid_point.Hitemindex+1, grid_point.Hindex)


    def streamstage_ts(self, reach):
        """
        Returns a dictionary with chainage as key and a time series with H as value for all grid points in the reach.
        """
        toreturn ={}
        for gp in reach.grid_points_h: #Loop all q-points
            ts = self.get_timeseries_H(gp) # get the discharge time series
            toreturn[gp.chainage]=ts
        return toreturn


    def to_csv(self, filename, ItemNumber, TimeStep=1):
        """
        Writes a csv file with reach name, chainage and Item value. Items counts from 1, TimeStep from 0
        """
        with codecs.open(filename, encoding='utf-8', mode='w') as file:
            datadic=self.get_values(ItemNumber, TimeStep)
            for name, chainlist in datadic.items():
                for cv in chainlist:
                    file.write(name + '\t' + str(cv[0]) + '\t' + str(cv[1]) + '\n')

    def to_shape(self, shape_file_name):
        """
        Writes a shape file with all grid points
        """
        import shapefile
        with shapefile.Writer(shape_file_name, shapeType=shapefile.POINT) as w:
                w.field('Branch', 'C')
                w.field('TopoID', 'C')
                w.field('Chainage', 'F', decimal=4)
                w.field('X', 'F', decimal=10)
                w.field('Y', 'F', decimal=10)
                w.field('PointType', 'C')
                w.field('PointTypeNumber', 'N')
                for r in self.reaches:
                    for p in r.grid_points:
                        w.record(r.name, r.topo_id, p.chainage,  p.x, p.y, p.point_type, p.type)
                        w.point(p.x, p.y)

Classes

class DataItem (name)
Source code
class DataItem(object):
    def __init__(self, name):
        self.name = name
        self.chainages =[]
        self.offset=0
        return super(DataItem, self).__init__()
class GridPoint (x=0, y=0, chainage=0, z=0.0, type=1)

A Gridpoint in a river

Source code
class GridPoint(RiverPoint):
    """
    A Gridpoint in a river
    """
    def __init__(self, x=0,y=0,chainage=0, z=0.0, type=1):
        super(GridPoint, self).__init__(x,y,chainage)
        self.z=z
        self.type=type
        self.index = -1
        self.itemindex = -1
        self.point_type=''
        self.name=''
        self.downstream=''

Ancestors

class Node (x=0, y=0, Type=1, name='')

description of class

Source code
class Node(Point):
    """description of class"""
    def __init__(self, x=0,y=0,Type=1,name=''):
        super(Node, self).__init__(x,y)
        self.type =Type
        self.name=name

Ancestors

class Point (x=0.0, y=0.0)
Source code
class Point(object):
    def __init__(self, x=0.0,y=0.0):
        self.x=x
        self.y=y

Subclasses

class Reach (type, name, topoid, flowdir)
Source code
class Reach(object):

    def __init__(self, type, name, topoid, flowdir):
        self.type = type
        self.id=-1
        self.name = name
        self.topo_id = topoid
        self.flow_direction = flowdir
        self.upstream_node=None
        self.downstream_node =None
        self.digi_points=[]
        self.grid_points=[]
        self.grid_points_h=[]
        self.grid_points_q=[]
        self.xsecs =[]
        self.dataitems={}
class RiverPoint (x=0, y=0, chainage=0.0)

A point in a river with a chainage

Source code
class RiverPoint(Point):
    """
    A point in a river with a chainage
    """
    def __init__(self, x = 0, y = 0, chainage=0.0):
        super(RiverPoint, self).__init__(x, y)
        self.chainage = chainage

Ancestors

Subclasses

class River_setup

A river setup consisting of nodes and reaches

Source code
class River_setup(object):
    """
    A river setup consisting of nodes and reaches
    """
    def __init__(self):
        self.nodes=[]
        self.reaches=[]
        return super(River_setup, self).__init__()

    @classmethod
    def from_file(cls, file_name):
        """
        Creates a river setup from either a .res1D-file or a .res11-file
        """
        import os
        from pathlib import Path
        if not os.path.isfile(file_name):
            raise FileNotFoundError(file_name)
        ext=Path(file_name).suffix.lower()
        cls = River_setup()
        if(ext == '.res1d'):
            cls.read_res1d(file_name)
            cls.make_unique_point_names()
        elif(ext == '.res11'):
            cls.read_res11(file_name)
        return cls;


    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.dispose()

    def dispose(self):
        self.res.dispose()

    def get_values(self, Item=1, TimeStep=0):
        """
        Returns the values for a particular time step and item in a double dictionary with first branch name and then chainage
        """
        toreturn={}
        for reach in self.reaches:
            if Item-1 in reach.dataitems:
                values = self.res.get_data(TimeStep,Item)[reach.dataitems[Item-1].offset: reach.dataitems[Item-1].offset + len(reach.dataitems[Item-1].chainages)]
                for i in range(0, len (values)):
                    if reach.name not in toreturn:
                        toreturn[reach.name] ={}
                    toreturn[reach.name][reach.dataitems[Item-1].chainages[i]]=values[i]
        for reach, val in toreturn.items():
            toreturn[reach]= sorted(val.items(), key=lambda x: x[0])
        return toreturn


    def read_res1d(self, Filename):
        """
        Reads a res1D file and builds the network
        """
        self.res= DFS.RES.from_file(Filename)
        nodestart=1

        NumberOfDynamicItems = self.res.StaticItems[nodestart].read_data()[0]
        self.DataItems =[]

        names = self.res.StaticItems[nodestart+1].read_data()
        names=names.split(';')
        for i in range(0, NumberOfDynamicItems):
            self.DataItems.append(DataItem(names[i].replace("\"", "")))
            self.DataItems[i].grouptype =self.res.StaticItems[nodestart+2].read_data()[i]
            self.DataItems[i].ItemIndex=i

        listlength = self.res.StaticItems[nodestart+5].read_data()
        nodestart+=6
        for i in range(0, NumberOfDynamicItems):
            if listlength[i]!=0:
                self.DataItems[i].ReachIndeces=self.res.StaticItems[nodestart].read_data()
                nodestart+=1

        for i in range(0, NumberOfDynamicItems):
            if self.DataItems[i].grouptype==2:
                self.DataItems[i].ReachNoVals=self.res.StaticItems[nodestart].read_data()
                self.DataItems[i].chainages=self.res.StaticItems[nodestart+1].read_data()
                nodestart+=2

        #Read nodes
        while self.res.StaticItems[nodestart].name!='NoNodes':
            nodestart+=1

        numberofnodes = self.res.StaticItems[nodestart].read_data()[0]
        xs =self.res.StaticItems[nodestart+3].read_data()
        ys =self.res.StaticItems[nodestart+4].read_data()
        types =self.res.StaticItems[nodestart+1].read_data()
        names = self.res.StaticItems[nodestart+2].read_data().split(';')
        for i in range(0,numberofnodes):
            self.nodes.append(Node(xs[i],ys[i],types[i], names[i]))

        #Read reaches
        reachstart= nodestart + 5
        numberofreaches = self.res.StaticItems[reachstart].read_data()[0]
        reachtypes = self.res.StaticItems[reachstart+1].read_data();
        allnames= self.res.StaticItems[reachstart+2].read_data()
        reachnames = allnames.split(';')
        topoids = self.res.StaticItems[reachstart+3].read_data().split(';')
        ups = self.res.StaticItems[reachstart+4].read_data()
        dws = self.res.StaticItems[reachstart+5].read_data()
        dir = self.res.StaticItems[reachstart+6].read_data()
        nodigipoints = self.res.StaticItems[reachstart+7].read_data()
        nogridpoints = self.res.StaticItems[reachstart+8].read_data()
        for i in range(0,numberofreaches):
            r = Reach(reachtypes[i], reachnames[i].replace("\"", ""), topoids[i].replace("\"", ""), dir[i])
            r.upstream_node = self.nodes[ups[i]]
            r.downstream_node = self.nodes[dws[i]]
            r.number_of_digi_points=nodigipoints[i]
            r.number_of_grid_points=nogridpoints[i]
            self.reaches.append(r)

        for di in self.DataItems:
            if di.grouptype==2:
                if not hasattr(di, 'ReachIndeces'):
                    di.ReachIndeces=range(0,numberofreaches)
                lcount=0
                for i in di.ReachIndeces:
                    ldi =DataItem(di.name)
                    ldi.ItemIndex =di.ItemIndex
                    ldi.offset =di.offset
                    ldi.chainages = di.chainages[di.offset:di.offset+di.ReachNoVals[lcount]]
                    di.offset +=di.ReachNoVals[lcount]
                    lcount+=1
                    self.reaches[i].dataitems[di.ItemIndex]=ldi

        qitem = next(i for i in self.DataItems if i.name =='Discharge')
        hitem = next(i for i in self.DataItems if i.name =='Water level' and i.grouptype==2)

        #Now build each individual reach
        Hindex=0
        Qindex=0
        rcount=0
        offset = reachstart + 9
        for r in self.reaches:
            if self.res.StaticItems[offset].name!='dpChainages':
                k=1

            #The digipoints
            dpChainages = self.res.StaticItems[offset].read_data()
            dpXs = self.res.StaticItems[offset+1].read_data()
            dpYs = self.res.StaticItems[offset+2].read_data()
            for i in range(0, r.number_of_digi_points):
                dp = RiverPoint(dpXs[i],dpYs[i], dpChainages[i])
                r.digi_points.append(dp)

            #The gridpoints
            gpType = self.res.StaticItems[offset+3].read_data()
            gpChainages = self.res.StaticItems[offset+4].read_data()
            gpXs = self.res.StaticItems[offset+5].read_data()
            gpYs = self.res.StaticItems[offset+6].read_data()
            gpZs = self.res.StaticItems[offset+7].read_data()
            for i in range(0, r.number_of_grid_points):
                dg = GridPoint(gpXs[i],gpYs[i], gpChainages[i], gpZs[i], gpType[i])
                dg.name = r.topo_id +"_" + r.name+"_" + str(dg.chainage)
                r.grid_points.append(dg)

            for dataitem in r.dataitems.values():
                for ci in range(len(dataitem.chainages)):
                    dg = next(dgp for dgp in r.grid_points if np.float32(dgp.chainage) == dataitem.chainages[ci])

                    if dataitem.name == "Water level":
                        dg.Hitemindex = dataitem.ItemIndex
                        dg.Hindex = ci + dataitem.offset;
                        r.grid_points_h.append(dg)
                        dg.point_type+='H'
                    elif dataitem.name == "Discharge":
                        dg.itemindex = dataitem.ItemIndex
                        dg.index = ci + dataitem.offset;
                        r.grid_points_q.append(dg)
                        dg.point_type+='Q'

          
                 
            #Some structures
            if self.res.StaticItems[offset+8].name=='structureNumberOfSubTypes':
                offset +=3
            
            #We may already be at the next reach
            if self.res.StaticItems[offset+10].name=='dpChainages':
                offset+=10
            elif self.res.StaticItems[offset+8].name=='dpChainages':
                offset+=8
            else: #Now for xsecs
                csIDs = self.res.StaticItems[offset+8].read_data().split(';')
                csnopoints= self.res.StaticItems[offset+10].read_data()

                cstypes= self.res.StaticItems[offset+9].read_data()
                csXs = self.res.StaticItems[offset+11].read_data()
                csZs = self.res.StaticItems[offset+12].read_data()
                csNoMarkers = self.res.StaticItems[offset+13].read_data()
                csMarkers = self.res.StaticItems[offset+14].read_data()
                csMarkersIndices = self.res.StaticItems[offset+15].read_data()
                csstart=0
                csmarkstart =0
                gpoffset=0
                for i in range(0, len(csnopoints)):
                    if cstypes[i+gpoffset] == 1000:
                        gpoffset+=1
                    cs = xsec(csIDs[i+gpoffset].replace("\"", ""), csnopoints[i])
                    cs.type = cstypes[i]
                    for n in range(csstart, csstart+ cs.number_of_points):
                        cs.xsec_points.append(xsecpoint(csXs[n],csZs[n]))
                    r.xsecs.append(cs);
                    csstart+=cs.number_of_points
                    for k in range(csmarkstart, csmarkstart + csNoMarkers[i]):
                        cs.xsec_points[csMarkersIndices[k]].marker=csMarkers[k]
                    csmarkstart += csNoMarkers[i]
                    cs.Gridpoint = [gp for gp in r.grid_points if gp.type==1025][i+gpoffset]

                offset+=16


    def make_unique_point_names(self):
        """
        Creates unique point names by adding "_Downstream" to the point with the highest flow of two duplicate gridpoints
        This is necessary because MikeHydro has duplicate points (TopoId, Branch, chainage) for at least all reach conversions
        """
        from functools import reduce
        point_dict={}
        for p in reduce(list.__add__,[r.grid_points_q for r in self.reaches]):
            if p.name in point_dict:
                if self.get_timeseries_Q(point_dict[p.name]).mean()>=self.get_timeseries_Q(p).mean():
                    pup = point_dict[p.name]
                    pup.name = p.name + '_Downstream';
                    pup.downstream='Downstream'
                    point_dict[pup.name]=pup;
                else:
                    p.downstream='Downstream'
                    p.name = p.name + '_Downstream';
            point_dict[p.name]=p;


    def read_res11(self, Filename):
        """
        Reads a res11 file and builds the network
        """
        self.res= DFS.RES.from_file(Filename)
        nodestart=0

        NumberOfstaticItemsPrBranch = self.res.StaticItems[0].read_data()[0]
        NumberOfBranches = self.res.StaticItems[1].read_data()[0]
        self.DataItems =[]

        nitems = len(self.res.items);

        nodestart =4
        while len(self.reaches)<NumberOfBranches:
            name = self.res.StaticItems[nodestart].read_data();
            topoid = self.res.StaticItems[nodestart+1].read_data();
            r = Reach(1, name, topoid, 1)
            self.reaches.append(r)
            gpChainages =self.res.StaticItems[nodestart+2].read_data()
            r.number_of_grid_points = len(gpChainages)
            gpXs =self.res.StaticItems[nodestart+3].read_data()
            gpYs =self.res.StaticItems[nodestart+4].read_data()
            gpZs =self.res.StaticItems[nodestart+5].read_data()
            gpRightBanks =self.res.StaticItems[nodestart+6].read_data()
            gpLeftBanks =self.res.StaticItems[nodestart+7].read_data()
            gptypes = self.res.StaticItems[nodestart+8].read_data()

            dpChainages =self.res.StaticItems[nodestart+9].read_data()
            r.number_of_digi_points = len(dpChainages)
            dpXs =self.res.StaticItems[nodestart+10].read_data()
            dpYs =self.res.StaticItems[nodestart+11].read_data()


            gpNumberOfRawdata = self.res.StaticItems[nodestart+13].read_data()
            connections = self.res.StaticItems[nodestart+12].read_data()
            # crosssections
            nodestart+=14
            while self.res.StaticItems[nodestart].name =='Cross section ID':
                crosssectionid = self.res.StaticItems[nodestart].read_data()
                rawXs = self.res.StaticItems[nodestart+1].read_data()
                rawZs = self.res.StaticItems[nodestart+2].read_data()
                nodestart+=4
            nodestart+=9

            #now build the reach
            Hindex=0
            Qindex=0
            for i in range(0, r.number_of_grid_points):
                dg = GridPoint(gpXs[i],gpYs[i], gpChainages[i], gpZs[i], gptypes[i])
                r.grid_points.append(dg)
                if dg.type==2:
                    r.grid_points_q.append(dg)
                    dg.index=Qindex
                    dg.itemindex = len(self.reaches) + int(nitems/2)
                    dg.point_type='Q'
                    Qindex+=1
                else:
                    r.grid_points_h.append(dg)
                    dg.Hindex = Hindex
                    dg.Hitemindex = len(self.reaches)
                    dg.point_type='H'
                    Hindex+=1

            for i in range(0, r.number_of_digi_points):
                dp = RiverPoint(dpXs[i],dpYs[i], dpChainages[i])
                r.digi_points.append(dp)

    def get_timeseries_Q(self, grid_point):
        """
        Returns a timeseries with flow data for a single grid point
        """
        if ( 'Q' in grid_point.point_type):
            #We need to add 1 because we have the item index not the item number
            return self.res.get_timeseries(grid_point.itemindex+1, grid_point.index)

    def get_timeseries_H(self, grid_point):
        """
        Returns a timeseries with water level data for a single grid point
        """
        if ( 'H' in grid_point.point_type):
            #We need to add 1 because we have the item index not the item number
            return self.res.get_timeseries(grid_point.Hitemindex+1, grid_point.Hindex)


    def streamstage_ts(self, reach):
        """
        Returns a dictionary with chainage as key and a time series with H as value for all grid points in the reach.
        """
        toreturn ={}
        for gp in reach.grid_points_h: #Loop all q-points
            ts = self.get_timeseries_H(gp) # get the discharge time series
            toreturn[gp.chainage]=ts
        return toreturn


    def to_csv(self, filename, ItemNumber, TimeStep=1):
        """
        Writes a csv file with reach name, chainage and Item value. Items counts from 1, TimeStep from 0
        """
        with codecs.open(filename, encoding='utf-8', mode='w') as file:
            datadic=self.get_values(ItemNumber, TimeStep)
            for name, chainlist in datadic.items():
                for cv in chainlist:
                    file.write(name + '\t' + str(cv[0]) + '\t' + str(cv[1]) + '\n')

    def to_shape(self, shape_file_name):
        """
        Writes a shape file with all grid points
        """
        import shapefile
        with shapefile.Writer(shape_file_name, shapeType=shapefile.POINT) as w:
                w.field('Branch', 'C')
                w.field('TopoID', 'C')
                w.field('Chainage', 'F', decimal=4)
                w.field('X', 'F', decimal=10)
                w.field('Y', 'F', decimal=10)
                w.field('PointType', 'C')
                w.field('PointTypeNumber', 'N')
                for r in self.reaches:
                    for p in r.grid_points:
                        w.record(r.name, r.topo_id, p.chainage,  p.x, p.y, p.point_type, p.type)
                        w.point(p.x, p.y)

Static methods

def from_file(file_name)

Creates a river setup from either a .res1D-file or a .res11-file

Source code
@classmethod
def from_file(cls, file_name):
    """
    Creates a river setup from either a .res1D-file or a .res11-file
    """
    import os
    from pathlib import Path
    if not os.path.isfile(file_name):
        raise FileNotFoundError(file_name)
    ext=Path(file_name).suffix.lower()
    cls = River_setup()
    if(ext == '.res1d'):
        cls.read_res1d(file_name)
        cls.make_unique_point_names()
    elif(ext == '.res11'):
        cls.read_res11(file_name)
    return cls;

Methods

def dispose(self)
Source code
def dispose(self):
    self.res.dispose()
def get_timeseries_H(self, grid_point)

Returns a timeseries with water level data for a single grid point

Source code
def get_timeseries_H(self, grid_point):
    """
    Returns a timeseries with water level data for a single grid point
    """
    if ( 'H' in grid_point.point_type):
        #We need to add 1 because we have the item index not the item number
        return self.res.get_timeseries(grid_point.Hitemindex+1, grid_point.Hindex)
def get_timeseries_Q(self, grid_point)

Returns a timeseries with flow data for a single grid point

Source code
def get_timeseries_Q(self, grid_point):
    """
    Returns a timeseries with flow data for a single grid point
    """
    if ( 'Q' in grid_point.point_type):
        #We need to add 1 because we have the item index not the item number
        return self.res.get_timeseries(grid_point.itemindex+1, grid_point.index)
def get_values(self, Item=1, TimeStep=0)

Returns the values for a particular time step and item in a double dictionary with first branch name and then chainage

Source code
def get_values(self, Item=1, TimeStep=0):
    """
    Returns the values for a particular time step and item in a double dictionary with first branch name and then chainage
    """
    toreturn={}
    for reach in self.reaches:
        if Item-1 in reach.dataitems:
            values = self.res.get_data(TimeStep,Item)[reach.dataitems[Item-1].offset: reach.dataitems[Item-1].offset + len(reach.dataitems[Item-1].chainages)]
            for i in range(0, len (values)):
                if reach.name not in toreturn:
                    toreturn[reach.name] ={}
                toreturn[reach.name][reach.dataitems[Item-1].chainages[i]]=values[i]
    for reach, val in toreturn.items():
        toreturn[reach]= sorted(val.items(), key=lambda x: x[0])
    return toreturn
def make_unique_point_names(self)

Creates unique point names by adding "_Downstream" to the point with the highest flow of two duplicate gridpoints This is necessary because MikeHydro has duplicate points (TopoId, Branch, chainage) for at least all reach conversions

Source code
def make_unique_point_names(self):
    """
    Creates unique point names by adding "_Downstream" to the point with the highest flow of two duplicate gridpoints
    This is necessary because MikeHydro has duplicate points (TopoId, Branch, chainage) for at least all reach conversions
    """
    from functools import reduce
    point_dict={}
    for p in reduce(list.__add__,[r.grid_points_q for r in self.reaches]):
        if p.name in point_dict:
            if self.get_timeseries_Q(point_dict[p.name]).mean()>=self.get_timeseries_Q(p).mean():
                pup = point_dict[p.name]
                pup.name = p.name + '_Downstream';
                pup.downstream='Downstream'
                point_dict[pup.name]=pup;
            else:
                p.downstream='Downstream'
                p.name = p.name + '_Downstream';
        point_dict[p.name]=p;
def read_res11(self, Filename)

Reads a res11 file and builds the network

Source code
def read_res11(self, Filename):
    """
    Reads a res11 file and builds the network
    """
    self.res= DFS.RES.from_file(Filename)
    nodestart=0

    NumberOfstaticItemsPrBranch = self.res.StaticItems[0].read_data()[0]
    NumberOfBranches = self.res.StaticItems[1].read_data()[0]
    self.DataItems =[]

    nitems = len(self.res.items);

    nodestart =4
    while len(self.reaches)<NumberOfBranches:
        name = self.res.StaticItems[nodestart].read_data();
        topoid = self.res.StaticItems[nodestart+1].read_data();
        r = Reach(1, name, topoid, 1)
        self.reaches.append(r)
        gpChainages =self.res.StaticItems[nodestart+2].read_data()
        r.number_of_grid_points = len(gpChainages)
        gpXs =self.res.StaticItems[nodestart+3].read_data()
        gpYs =self.res.StaticItems[nodestart+4].read_data()
        gpZs =self.res.StaticItems[nodestart+5].read_data()
        gpRightBanks =self.res.StaticItems[nodestart+6].read_data()
        gpLeftBanks =self.res.StaticItems[nodestart+7].read_data()
        gptypes = self.res.StaticItems[nodestart+8].read_data()

        dpChainages =self.res.StaticItems[nodestart+9].read_data()
        r.number_of_digi_points = len(dpChainages)
        dpXs =self.res.StaticItems[nodestart+10].read_data()
        dpYs =self.res.StaticItems[nodestart+11].read_data()


        gpNumberOfRawdata = self.res.StaticItems[nodestart+13].read_data()
        connections = self.res.StaticItems[nodestart+12].read_data()
        # crosssections
        nodestart+=14
        while self.res.StaticItems[nodestart].name =='Cross section ID':
            crosssectionid = self.res.StaticItems[nodestart].read_data()
            rawXs = self.res.StaticItems[nodestart+1].read_data()
            rawZs = self.res.StaticItems[nodestart+2].read_data()
            nodestart+=4
        nodestart+=9

        #now build the reach
        Hindex=0
        Qindex=0
        for i in range(0, r.number_of_grid_points):
            dg = GridPoint(gpXs[i],gpYs[i], gpChainages[i], gpZs[i], gptypes[i])
            r.grid_points.append(dg)
            if dg.type==2:
                r.grid_points_q.append(dg)
                dg.index=Qindex
                dg.itemindex = len(self.reaches) + int(nitems/2)
                dg.point_type='Q'
                Qindex+=1
            else:
                r.grid_points_h.append(dg)
                dg.Hindex = Hindex
                dg.Hitemindex = len(self.reaches)
                dg.point_type='H'
                Hindex+=1

        for i in range(0, r.number_of_digi_points):
            dp = RiverPoint(dpXs[i],dpYs[i], dpChainages[i])
            r.digi_points.append(dp)
def read_res1d(self, Filename)

Reads a res1D file and builds the network

Source code
def read_res1d(self, Filename):
    """
    Reads a res1D file and builds the network
    """
    self.res= DFS.RES.from_file(Filename)
    nodestart=1

    NumberOfDynamicItems = self.res.StaticItems[nodestart].read_data()[0]
    self.DataItems =[]

    names = self.res.StaticItems[nodestart+1].read_data()
    names=names.split(';')
    for i in range(0, NumberOfDynamicItems):
        self.DataItems.append(DataItem(names[i].replace("\"", "")))
        self.DataItems[i].grouptype =self.res.StaticItems[nodestart+2].read_data()[i]
        self.DataItems[i].ItemIndex=i

    listlength = self.res.StaticItems[nodestart+5].read_data()
    nodestart+=6
    for i in range(0, NumberOfDynamicItems):
        if listlength[i]!=0:
            self.DataItems[i].ReachIndeces=self.res.StaticItems[nodestart].read_data()
            nodestart+=1

    for i in range(0, NumberOfDynamicItems):
        if self.DataItems[i].grouptype==2:
            self.DataItems[i].ReachNoVals=self.res.StaticItems[nodestart].read_data()
            self.DataItems[i].chainages=self.res.StaticItems[nodestart+1].read_data()
            nodestart+=2

    #Read nodes
    while self.res.StaticItems[nodestart].name!='NoNodes':
        nodestart+=1

    numberofnodes = self.res.StaticItems[nodestart].read_data()[0]
    xs =self.res.StaticItems[nodestart+3].read_data()
    ys =self.res.StaticItems[nodestart+4].read_data()
    types =self.res.StaticItems[nodestart+1].read_data()
    names = self.res.StaticItems[nodestart+2].read_data().split(';')
    for i in range(0,numberofnodes):
        self.nodes.append(Node(xs[i],ys[i],types[i], names[i]))

    #Read reaches
    reachstart= nodestart + 5
    numberofreaches = self.res.StaticItems[reachstart].read_data()[0]
    reachtypes = self.res.StaticItems[reachstart+1].read_data();
    allnames= self.res.StaticItems[reachstart+2].read_data()
    reachnames = allnames.split(';')
    topoids = self.res.StaticItems[reachstart+3].read_data().split(';')
    ups = self.res.StaticItems[reachstart+4].read_data()
    dws = self.res.StaticItems[reachstart+5].read_data()
    dir = self.res.StaticItems[reachstart+6].read_data()
    nodigipoints = self.res.StaticItems[reachstart+7].read_data()
    nogridpoints = self.res.StaticItems[reachstart+8].read_data()
    for i in range(0,numberofreaches):
        r = Reach(reachtypes[i], reachnames[i].replace("\"", ""), topoids[i].replace("\"", ""), dir[i])
        r.upstream_node = self.nodes[ups[i]]
        r.downstream_node = self.nodes[dws[i]]
        r.number_of_digi_points=nodigipoints[i]
        r.number_of_grid_points=nogridpoints[i]
        self.reaches.append(r)

    for di in self.DataItems:
        if di.grouptype==2:
            if not hasattr(di, 'ReachIndeces'):
                di.ReachIndeces=range(0,numberofreaches)
            lcount=0
            for i in di.ReachIndeces:
                ldi =DataItem(di.name)
                ldi.ItemIndex =di.ItemIndex
                ldi.offset =di.offset
                ldi.chainages = di.chainages[di.offset:di.offset+di.ReachNoVals[lcount]]
                di.offset +=di.ReachNoVals[lcount]
                lcount+=1
                self.reaches[i].dataitems[di.ItemIndex]=ldi

    qitem = next(i for i in self.DataItems if i.name =='Discharge')
    hitem = next(i for i in self.DataItems if i.name =='Water level' and i.grouptype==2)

    #Now build each individual reach
    Hindex=0
    Qindex=0
    rcount=0
    offset = reachstart + 9
    for r in self.reaches:
        if self.res.StaticItems[offset].name!='dpChainages':
            k=1

        #The digipoints
        dpChainages = self.res.StaticItems[offset].read_data()
        dpXs = self.res.StaticItems[offset+1].read_data()
        dpYs = self.res.StaticItems[offset+2].read_data()
        for i in range(0, r.number_of_digi_points):
            dp = RiverPoint(dpXs[i],dpYs[i], dpChainages[i])
            r.digi_points.append(dp)

        #The gridpoints
        gpType = self.res.StaticItems[offset+3].read_data()
        gpChainages = self.res.StaticItems[offset+4].read_data()
        gpXs = self.res.StaticItems[offset+5].read_data()
        gpYs = self.res.StaticItems[offset+6].read_data()
        gpZs = self.res.StaticItems[offset+7].read_data()
        for i in range(0, r.number_of_grid_points):
            dg = GridPoint(gpXs[i],gpYs[i], gpChainages[i], gpZs[i], gpType[i])
            dg.name = r.topo_id +"_" + r.name+"_" + str(dg.chainage)
            r.grid_points.append(dg)

        for dataitem in r.dataitems.values():
            for ci in range(len(dataitem.chainages)):
                dg = next(dgp for dgp in r.grid_points if np.float32(dgp.chainage) == dataitem.chainages[ci])

                if dataitem.name == "Water level":
                    dg.Hitemindex = dataitem.ItemIndex
                    dg.Hindex = ci + dataitem.offset;
                    r.grid_points_h.append(dg)
                    dg.point_type+='H'
                elif dataitem.name == "Discharge":
                    dg.itemindex = dataitem.ItemIndex
                    dg.index = ci + dataitem.offset;
                    r.grid_points_q.append(dg)
                    dg.point_type+='Q'

      
             
        #Some structures
        if self.res.StaticItems[offset+8].name=='structureNumberOfSubTypes':
            offset +=3
        
        #We may already be at the next reach
        if self.res.StaticItems[offset+10].name=='dpChainages':
            offset+=10
        elif self.res.StaticItems[offset+8].name=='dpChainages':
            offset+=8
        else: #Now for xsecs
            csIDs = self.res.StaticItems[offset+8].read_data().split(';')
            csnopoints= self.res.StaticItems[offset+10].read_data()

            cstypes= self.res.StaticItems[offset+9].read_data()
            csXs = self.res.StaticItems[offset+11].read_data()
            csZs = self.res.StaticItems[offset+12].read_data()
            csNoMarkers = self.res.StaticItems[offset+13].read_data()
            csMarkers = self.res.StaticItems[offset+14].read_data()
            csMarkersIndices = self.res.StaticItems[offset+15].read_data()
            csstart=0
            csmarkstart =0
            gpoffset=0
            for i in range(0, len(csnopoints)):
                if cstypes[i+gpoffset] == 1000:
                    gpoffset+=1
                cs = xsec(csIDs[i+gpoffset].replace("\"", ""), csnopoints[i])
                cs.type = cstypes[i]
                for n in range(csstart, csstart+ cs.number_of_points):
                    cs.xsec_points.append(xsecpoint(csXs[n],csZs[n]))
                r.xsecs.append(cs);
                csstart+=cs.number_of_points
                for k in range(csmarkstart, csmarkstart + csNoMarkers[i]):
                    cs.xsec_points[csMarkersIndices[k]].marker=csMarkers[k]
                csmarkstart += csNoMarkers[i]
                cs.Gridpoint = [gp for gp in r.grid_points if gp.type==1025][i+gpoffset]

            offset+=16
def streamstage_ts(self, reach)

Returns a dictionary with chainage as key and a time series with H as value for all grid points in the reach.

Source code
def streamstage_ts(self, reach):
    """
    Returns a dictionary with chainage as key and a time series with H as value for all grid points in the reach.
    """
    toreturn ={}
    for gp in reach.grid_points_h: #Loop all q-points
        ts = self.get_timeseries_H(gp) # get the discharge time series
        toreturn[gp.chainage]=ts
    return toreturn
def to_csv(self, filename, ItemNumber, TimeStep=1)

Writes a csv file with reach name, chainage and Item value. Items counts from 1, TimeStep from 0

Source code
def to_csv(self, filename, ItemNumber, TimeStep=1):
    """
    Writes a csv file with reach name, chainage and Item value. Items counts from 1, TimeStep from 0
    """
    with codecs.open(filename, encoding='utf-8', mode='w') as file:
        datadic=self.get_values(ItemNumber, TimeStep)
        for name, chainlist in datadic.items():
            for cv in chainlist:
                file.write(name + '\t' + str(cv[0]) + '\t' + str(cv[1]) + '\n')
def to_shape(self, shape_file_name)

Writes a shape file with all grid points

Source code
def to_shape(self, shape_file_name):
    """
    Writes a shape file with all grid points
    """
    import shapefile
    with shapefile.Writer(shape_file_name, shapeType=shapefile.POINT) as w:
            w.field('Branch', 'C')
            w.field('TopoID', 'C')
            w.field('Chainage', 'F', decimal=4)
            w.field('X', 'F', decimal=10)
            w.field('Y', 'F', decimal=10)
            w.field('PointType', 'C')
            w.field('PointTypeNumber', 'N')
            for r in self.reaches:
                for p in r.grid_points:
                    w.record(r.name, r.topo_id, p.chainage,  p.x, p.y, p.point_type, p.type)
                    w.point(p.x, p.y)
class xsec (name, NumberOfPoints)

A Cross section

Source code
class xsec(object):
    """
    A Cross section
    """
    def __init__(self, name, NumberOfPoints):
        self.name =name
        self.number_of_points = NumberOfPoints
        self.xsec_points=[]
        self.type =-1
class xsecpoint (x, z)

A point in a cross section. Has x and z

Source code
class xsecpoint(object):
    """
    A point in a cross section. Has x and z
    """
    def __init__(self, x, z):
        self.x=x
        self.z=z
        self.marker=-1