diff --git a/src/Checker/Mage.py b/src/Checker/Mage.py new file mode 100644 index 0000000000000000000000000000000000000000..224253dfc57a0ba94df2fd585b1eef24836b7f12 --- /dev/null +++ b/src/Checker/Mage.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- + +import time + +from queue import Queue +from tools import flatten +from functools import reduce + +from PyQt5.QtCore import QCoreApplication + +from Checker.Checker import AbstractModelChecker, STATUS + +_translate = QCoreApplication.translate + +class MageNetworkGraphChecker(AbstractModelChecker): + def __init__(self, connectivity = True): + super(MageNetworkGraphChecker, self).__init__() + + self._mode_conn = connectivity + + if connectivity: + mode = "connectivity" + else: + mode = "cycle" + + self._name = _translate("Checker", f"Mage network graph {mode} checker") + self._description = _translate("Checker", "Check if the network graph is valid") + + def _connectivity(self, summary, status, graph): + # Keep only enabled edges + edges = list( + filter( + lambda e: e.is_enable(), + graph.edges() + ) + ) + # Get all related nodes + nodes = list( + set( + flatten( + map( + lambda e: [e.node1, e.node2], + edges + ) + ) + ) + ) + + # Visite graph + q = Queue() + for node in nodes: + if graph.is_upstream_node(node): + q.put(node) + break # We take only one node + + if q.qsize() == 0: + summary = "no_upstream_node" + status = STATUS.ERROR + return summary, status + + visited = set() + while q.qsize() != 0: + current = q.get() + if current is None: + continue + + if current in visited: + continue + + related_edges = list( + filter( + lambda e: e.node1 == current or e.node2 == current, + edges + ) + ) + + # Get next node(s) to visite + nexts = flatten( + map( + lambda e: [e.node1, e.node2], + related_edges + ) + ) + + for n in nexts: + q.put(n) + + # Visited node + visited.add(current) + + if len(visited) != len(nodes): + if "ok" in summary: + summary = "network_connectivity" + else: + summary = summary + "|" + "network_connectivity" + status = STATUS.ERROR + return summary, status + + return summary, status + + def _cycle(self, summary, status, graph): + # Keep only enabled edges + edges = list( + filter( + lambda e: e.is_enable(), + graph.edges() + ) + ) + # Get all related nodes + nodes = list( + set( + flatten( + map( + lambda e: [e.node1, e.node2], + edges + ) + ) + ) + ) + + + for edge in edges: + # Visite graph + q = Queue() + initial = edge.node1 + q.put(initial) + + visited = set() + while q.qsize() != 0: + current = q.get() + if current is None: + continue + + if current in visited: + continue + + related_edges = list( + filter( + lambda e: e.node1 == current, + edges + ) + ) + + # Get next node(s) to visite + nexts = list( + map( + lambda e: e.node2, + related_edges + ) + ) + + # The initial node cannot be visited a second time where visite + # started by this node, otherelse there is a cycle in the graph + if initial in nexts: + if "ok" in summary: + summary = "cycle_detected" + else: + summary = summary + "|" + "cycle_detected" + status = STATUS.ERROR + return summary, status + + for n in nexts: + q.put(n) + + # Visited node + visited.add(current) + + return summary, status + + def run(self, study): + summary = "ok" + status = STATUS.OK + + if study is None: + self._status = STATUS.ERROR + self._summary = "invalid_study" + return False + + river = study.river + if river is None: + self._status = STATUS.ERROR + self._summary = "no_river_found" + return False + + edges = list(filter(lambda e: e.is_enable(), river.edges())) + if len(edges) == 0: + self._status = STATUS.ERROR + self._summary = "no_reach_defined" + return False + + if self._mode_conn: + summary, status = self._connectivity(summary, status, river) + else: + summary, status = self._cycle(summary, status, river) + + self._summary = summary + self._status = status + return True diff --git a/src/Solver/ASolver.py b/src/Solver/ASolver.py index 7cef9f6a63287670f654fa7f2634f32d2068602e..ad7302bb03c0e544233162d072af70f3d782a19f 100644 --- a/src/Solver/ASolver.py +++ b/src/Solver/ASolver.py @@ -52,7 +52,7 @@ class AbstractSolver(object): @classmethod def default_parameters(cls): lst = [ - ("all_init_time", "00:00:00:00"), + ("all_init_time", "000:00:00:00"), ("all_final_time", "999:99:00:00"), ("all_timestep", "300.0"), ] @@ -122,6 +122,17 @@ class AbstractSolver(object): def export(self, study, repertory, qlog = None): raise NotImplementedMethodeError(self, self.export) + def input_param(self): + """Return input command line parameter(s) + + Args: + study: The study object + + Returns: + Returns input parameter(s) string + """ + raise NotImplementedMethodeError(self, self.input_param) + ####### # Run # ####### @@ -137,8 +148,10 @@ class AbstractSolver(object): return True cmd = self._cmd_solver - cmd = cmd.replace("@path", self._path_solver).split() + cmd = cmd.replace("@path", self._path_solver) + cmd = cmd.replace("@input", self.input_param()) + cmd = cmd.split() exe = cmd[0] args = cmd[1:] diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py index 8e81e6ced7f460d0480c8c7976bee8f2e4b3b8b6..54a0eb25d64b9cdee5225ec9381f69a1899994c6 100644 --- a/src/Solver/Mage.py +++ b/src/Solver/Mage.py @@ -5,6 +5,7 @@ import os from tools import timer from Solver.ASolver import AbstractSolver +from Checker.Mage import MageNetworkGraphChecker class Mage(AbstractSolver): def __init__(self, name): @@ -49,10 +50,22 @@ class Mage(AbstractSolver): return lst + @classmethod + def checkers(cls): + lst = [ + MageNetworkGraphChecker(connectivity = True), + MageNetworkGraphChecker(connectivity = False) + ] + + return lst + ########## # Export # ########## + def input_param(self): + return "0.REP" + @timer def _export_ST(self, study, repertory, qlog): files = []