Beispiel #1
0
class ResultFinder(object):
    """Class storing a Mike1D data item and a corresponding element index"""
    def __init__(self, filename, useFilter=None, outputDataItem=True):
        # Load result file
        self.diagnostics = Diagnostics("Loading file")
        self.resultData = ResultData()
        self.resultData.Connection = Connection.Create(filename)
        self.useFilter = useFilter
        self.outputDataItem = outputDataItem

        if useFilter:
            self.SetupFilter()
        else:
            self.Load()

        # Searcher is helping to find reaches, nodes and catchments
        self.searcher = ResultDataSearch(self.resultData)

    def SetupFilter(self):
        """
        Setup the filter for result data object.
        """
        if not self.useFilter:
            return

        self.resultData.LoadHeader(True, self.diagnostics)

        self.dataFilter = Filter()
        self.dataSubFilter = DataItemFilterName(self.resultData)
        self.dataFilter.AddDataItemFilter(self.dataSubFilter)

        self.resultData.Parameters.Filter = self.dataFilter

    def Load(self):
        """
        Load the data from the result file into memory
        """
        if self.useFilter:
            self.resultData.LoadData(self.diagnostics)
        else:
            self.resultData.Load(self.diagnostics)

    def AddLocation(self, locationType, locationId):
        if locationType == LocationType.REACH:
            self.AddReach(locationId)

        if locationType == LocationType.NODE:
            self.AddNode(locationId)

        if locationType == LocationType.CATCHMENT:
            self.AddCatchment(locationId)

    def AddReach(self, reachId):
        self.dataSubFilter.Reaches.Add(reachId)

    def AddNode(self, nodeId):
        self.dataSubFilter.Nodes.Add(nodeId)

    def AddCatchment(self, catchmentId):
        self.dataSubFilter.Catchments.Add(catchmentId)

    def FindQuantity(self, dataSet, quantityId):
        """
        Find a given quantity from an IRes1DDataSet
        """
        numItems = dataSet.DataItems.Count
        dataItems = list(dataSet.DataItems)
        for j in range(numItems):
            dataItem = dataItems[j]
            if StringComparer.OrdinalIgnoreCase.Equals(dataItem.Quantity.Id,
                                                       quantityId):
                return dataItem
        return None

    def FindQuantityInLocation(self,
                               locationType,
                               quantityId,
                               locationId,
                               chainage=Constants.ALL_CHAINAGES):
        data = None

        if locationType == LocationType.REACH:
            if chainage == Constants.ALL_CHAINAGES:
                data = self.FindReachQuantityAllChainages(
                    quantityId, locationId)
            else:
                data = self.FindReachQuantity(quantityId, locationId, chainage)

        if locationType == LocationType.NODE:
            data = self.FindNodeQuantity(quantityId, locationId)

        if locationType == LocationType.CATCHMENT:
            data = self.FindCatchmentQuantity(quantityId, locationId)

        return data

    def FindReachQuantityAllChainages(self, quantityId, reachId):
        # There can be more than one reach with this reachId, check all
        reaches = self.searcher.FindReaches(reachId)
        if reaches.Count == 0:
            print("Could not find reach '%s'" % (reachId))
            return None

        dataEntries = []
        # All elements of all reaches having that quantity
        for reach in reaches:
            dataItem = self.FindQuantity(reach, quantityId)
            if dataItem == None:
                continue

            for j in range(dataItem.NumberOfElements):
                dataEntry = self.ConvertDataItemElementToList(dataItem, j)
                dataEntries.append(dataEntry)

        if len(dataEntries) == 0:
            print("Could not find quantity '%s' on reach '%s'." %
                  (quantityId, reachId))

        return dataEntries

    def FindReachQuantity(self, quantityId, reachId, chainage):
        """
        Find a given quantity on the reach with the given reachId.
        The grid point closest to the given chainage is used.
        """
        # There can be more than one reach with this reachId, check all
        reaches = self.searcher.FindReaches(reachId)
        if reaches.Count == 0:
            print("Could not find reach '%s'" % (reachId))
            return None

        # Find grid point closest to given chainage
        minDist = 999999
        minDataItem = None
        minElmtIndex = -1
        for reach in reaches:
            dataItem = self.FindQuantity(reach, quantityId)
            if dataItem != None:
                # Loop over all grid points in reach dataItem
                for j in range(dataItem.NumberOfElements):
                    indexList = list(dataItem.IndexList)
                    gridPoints = list(reach.GridPoints)
                    dist = abs(gridPoints[indexList[j]].Chainage - chainage)
                    if dist < minDist:
                        minDist = dist
                        minDataItem = dataItem
                        minElmtIndex = j

        if minDataItem == None:
            print("Could not find quantity '%s' on reach '%s'." %
                  (quantityId, reachId))

        return [self.ConvertDataItemElementToList(dataItem, minElmtIndex)]

    def FindNodeQuantity(self, quantityId, nodeId):
        """
        Find a given quantity on the node with the given nodeId
        """
        node = self.searcher.FindNode(nodeId)

        if node == None:
            print("Could not find node '%s'" % (nodeId))
            return None

        dataItem = self.FindQuantity(node, quantityId)
        if dataItem == None:
            print("Could not find quantity '%s' in node '%s'." %
                  (quantityId, nodeId))

        return [self.ConvertDataItemElementToList(dataItem, 0)]

    def FindCatchmentQuantity(self, quantityId, catchId):
        """
        Find a given quantity on the catchment with the given catchId
        """
        catchment = self.searcher.FindCatchment(catchId)
        if catchment == None:
            print("Could not find catchment '%s'" % (catchId))
            return None

        dataItem = self.FindQuantity(catchment, quantityId)
        if dataItem == None:
            print("Could not find quantity '%s' in catchment '%s'." %
                  (quantityId, catchId))

        return [self.ConvertDataItemElementToList(dataItem, 0)]

    def PrintAllQuantities(self):
        """
        Print out quantities on IRes1DDataSet
        """
        print("Available quantity IDs:")
        resultData = self.resultData
        quantities = list(resultData.Quantities)
        for quantity in quantities:
            print("  %s" % (quantity.Id))

    def PrintQuantities(self, locationType, locationId, chainage=0):
        dataSet = None

        if locationType == LocationType.REACH:
            dataSet = self.searcher.FindReaches(locationId)[0]

        if locationType == LocationType.NODE:
            dataSet = self.searcher.FindNode(locationId)

        if locationType == LocationType.CATCHMENT:
            dataSet = self.searcher.FindCatchment(locationId)

        if dataSet is None:
            return

        dataItems = list(dataSet.DataItems)
        for dataItem in dataItems:
            print("'%s'" % dataItem.Quantity.Id)

    def PrintAllLocations(self, locationType):
        if locationType == LocationType.NODE:
            self.PrintAllNodes()

        if locationType == LocationType.REACH:
            self.PrintAllReaches()

        if locationType == LocationType.CATCHMENT:
            self.PrintAllCatchments()

    def PrintAllReaches(self):
        resultData = self.resultData
        for j in range(resultData.Reaches.Count):
            reach = list(resultData.Reaches)[j]
            gridPoints = list(reach.GridPoints)
            startChainage = gridPoints[0].Chainage
            endChainage = gridPoints[-1].Chainage
            print("'%-30s (%9.2f - %9.2f)'" %
                  (reach.Name, startChainage, endChainage))

    def PrintAllNodes(self):
        resultData = self.resultData
        for j in range(resultData.Nodes.Count):
            node = list(resultData.Nodes)[j]
            print("'%s'" % node.Id)

    def PrintAllCatchments(self):
        resultData = self.resultData
        for j in range(resultData.Catchments.Count):
            catchment = list(resultData.Catchments)[j]
            print("'%s'" % catchment.Id)

    def ConvertDataItemElementToList(self, dataItem, elementIndex):
        """
        Convert dataItem element to list of numbers.
        """
        if self.outputDataItem:
            return DataEntry(dataItem, elementIndex)

        if dataItem is None:
            return None

        data = []
        for j in range(dataItem.NumberOfTimeSteps):
            data.append(dataItem.GetValue(j, elementIndex))
        return data

    def GetTimes(self, toTicks=True):
        """
        Get a list of times
        """
        timesList = list(self.resultData.TimesList)
        if toTicks:
            return list(map(lambda x: x.Ticks, timesList))
        else:
            return timesList
Beispiel #2
0
class Res1D:
    def __init__(self,
                 file_path=None,
                 lazy_load=False,
                 header_load=False,
                 reaches=None,
                 nodes=None,
                 catchments=None,
                 col_name_delimiter=NAME_DELIMITER,
                 put_chainage_in_col_name=True):

        self.file_path = file_path
        self._lazy_load = lazy_load

        self._reaches = reaches if reaches else []
        self._nodes = nodes if nodes else []
        self._catchments = catchments if catchments else []

        self._use_filter = (reaches is not None or nodes is not None
                            or catchments is not None)

        self._time_index = None
        self._start_time = None
        self._end_time = None

        self._queries = []

        self._load_header()
        if not header_load:
            self._load_file()

        self._col_name_delimiter = col_name_delimiter
        self._put_chainage_in_col_name = put_chainage_in_col_name

    def __repr__(self):
        out = ["<mikeio1d.Res1D>"]

        if self.file_path:
            out.append(f"Start time: {str(self.start_time)}")
            out.append(f"End time: {str(self.end_time)}")
            out.append(f"# Timesteps: {str(self.data.NumberOfTimeSteps)}")
            out.append(f"# Catchments: {self.data.Catchments.get_Count()}")
            out.append(f"# Nodes: {self.data.Nodes.get_Count()}")
            out.append(f"# Reaches: {self.data.Reaches.get_Count()}")

            out.append(f"# Globals: {self.data.GlobalData.DataItems.Count}")
            for i, quantity in enumerate(self.data.Quantities):
                out.append(
                    f"{i} - {quantity.Id} <{quantity.EumQuantity.UnitAbbreviation}>"
                )

        return str.join("\n", out)

    #region File loading

    def _load_header(self):
        if not os.path.exists(self.file_path):
            raise FileExistsError(f"File {self.file_path} does not exist.")

        self._data = ResultData()
        self._data.Connection = Connection.Create(self.file_path)
        self._diagnostics = Diagnostics("Loading header")

        if self._lazy_load:
            self._data.Connection.BridgeName = "res1dlazy"

        if self._use_filter:
            self._data.LoadHeader(True, self._diagnostics)
        else:
            self._data.LoadHeader(self._diagnostics)

    def _load_file(self):

        if self._use_filter:
            self._setup_filter()

            for reach in self._reaches:
                self._add_reach(reach)
            for node in self._nodes:
                self._add_node(node)
            for catchment in self._catchments:
                self._add_catchment(catchment)

            self._data.LoadData(self._diagnostics)
        else:
            self._data.Load(self._diagnostics)

        self._query = ResultDataQuery(self._data)

    def _setup_filter(self):
        """
        Setup the filter for result data object.
        """
        if not self._use_filter:
            return

        self._data_filter = Filter()
        self._data_subfilter = DataItemFilterName(self._data)
        self._data_filter.AddDataItemFilter(self._data_subfilter)

        self._data.Parameters.Filter = self._data_filter

    def _add_reach(self, reach_id):
        self._data_subfilter.Reaches.Add(reach_id)

    def _add_node(self, node_id):
        self._data_subfilter.Nodes.Add(node_id)

    def _add_catchment(self, catchment_id):
        self._data_subfilter.Catchments.Add(catchment_id)

    #endregion File loading

    def read(self, queries=None):
        """
        Read loaded .res1d file data.

        Parameters
        ----------
        queries: A single query or a list of queries.
        Default is None = reads all data.
        """

        if queries is None:
            return self.read_all()

        queries = queries if isinstance(queries, list) else [queries]

        dfs = []
        for query in queries:
            df = pd.DataFrame(index=self.time_index)
            df[str(query)] = query.get_values(self)
            dfs.append(df)

        return pd.concat(dfs, axis=1)

    def read_all(self):
        """ Read all data from res1d file to dataframe. """

        dfs = []
        for data_set in self.data.DataSets:

            # Skip filtered data sets
            name = Res1D.get_data_set_name(data_set)
            if self._use_filter and name not in self._catchments + self._reaches + self._nodes:
                continue

            for data_item in data_set.DataItems:
                values_name_pair = self.get_values(data_set, data_item)

                for values, col_name in values_name_pair:
                    df = pd.DataFrame(index=self.time_index)
                    df[col_name] = values
                    dfs.append(df)

        return pd.concat(dfs, axis=1)

    def get_values(self, data_set, data_item):
        """ Get all time series values in given data_item. """
        if data_item.IndexList is None:
            return self.get_scalar_value(data_set, data_item)
        else:
            return self.get_vector_values(data_set, data_item)

    def get_scalar_value(self, data_set, data_item):
        name = Res1D.get_data_set_name(data_set)
        quantity_id = data_item.Quantity.Id
        col_name = self._col_name_delimiter.join([quantity_id, name])
        element_index = 0

        yield data_item.CreateTimeSeriesData(element_index), col_name

    def get_vector_values(self, data_set, data_item):
        name = Res1D.get_data_set_name(data_set)
        chainages = data_set.GetChainages(data_item)

        for i in range(data_item.NumberOfElements):
            quantity_id = data_item.Quantity.Id
            postfix = f"{chainages[i]:g}" if self._put_chainage_in_col_name else str(
                i)
            col_name_i = self._col_name_delimiter.join(
                [quantity_id, name, postfix])

            yield data_item.CreateTimeSeriesData(i), col_name_i

    @staticmethod
    def get_data_set_name(data_set):
        name = data_set.Name if hasattr(data_set, "Name") else data_set.Id
        name = "" if name is None else name
        return name

    @property
    def time_index(self):
        """ pandas.DatetimeIndex of the time index. """
        if self._time_index is not None:
            return self._time_index

        time_stamps = [from_dotnet_datetime(t) for t in self.data.TimesList]
        self._time_index = pd.DatetimeIndex(time_stamps)
        return self._time_index

    @property
    def start_time(self):
        if self._start_time is not None:
            return self._start_time

        return from_dotnet_datetime(self.data.StartTime)

    @property
    def end_time(self):
        if self._end_time is not None:
            return self._end_time

        return from_dotnet_datetime(self.data.EndTime)

    @property
    def quantities(self):
        """ Quantities in res1d file. """
        return [quantity.Id for quantity in self._data.Quantities]

    @property
    def query(self):
        """
        .NET object ResultDataQuery to use for querying the loaded res1d data.

        More information about ResultDataQuery class see:
        https://manuals.mikepoweredbydhi.help/latest/General/Class_Library/DHI_MIKE1D/html/T_DHI_Mike1D_ResultDataAccess_ResultDataQuery.htm
        """
        return self._query

    @property
    def data(self):
        """
        .NET object ResultData with the loaded res1d data.

        More information about ResultData class see:
        https://manuals.mikepoweredbydhi.help/latest/General/Class_Library/DHI_MIKE1D/html/T_DHI_Mike1D_ResultDataAccess_ResultData.htm
        """
        return self._data

    @property
    def catchments(self):
        """ Catchments in res1d file. """
        return {
            Res1D.get_data_set_name(catchment): catchment
            for catchment in self._data.Catchments
        }

    @property
    def reaches(self):
        """ Reaches in res1d file. """
        return {
            Res1D.get_data_set_name(reach): reach
            for reach in self._data.Reaches
        }

    @property
    def nodes(self):
        """ Nodes in res1d file. """
        return {
            Res1D.get_data_set_name(node): node
            for node in self._data.Nodes
        }

    @property
    def global_data(self):
        """ Global data items in res1d file. """
        return {
            Res1D.get_data_set_name(gdat): gdat
            for gdat in self._data.GlobalData.DataItems
        }

    #region Query wrappers

    def get_catchment_values(self, catchment_id, quantity):
        return to_numpy(self.query.GetCatchmentValues(catchment_id, quantity))

    def get_node_values(self, node_id, quantity):
        return to_numpy(self.query.GetNodeValues(node_id, quantity))

    def get_reach_values(self, reach_name, chainage, quantity):
        return to_numpy(
            self.query.GetReachValues(reach_name, chainage, quantity))

    def get_reach_value(self, reach_name, chainage, quantity, time):
        time_dotnet = time if isinstance(
            time, DateTime) else to_dotnet_datetime(time)
        return self.query.GetReachValue(reach_name, chainage, quantity,
                                        time_dotnet)

    def get_reach_start_values(self, reach_name, quantity):
        return to_numpy(self.query.GetReachStartValues(reach_name, quantity))

    def get_reach_end_values(self, reach_name, quantity):
        return to_numpy(self.query.GetReachEndValues(reach_name, quantity))

    def get_reach_sum_values(self, reach_name, quantity):
        return to_numpy(self.query.GetReachSumValues(reach_name, quantity))