diff --git a/src/Checker/Checker.py b/src/Checker/Checker.py index ae6f04a25493d79fca25ea4a321121ab254ab8b4..fec6ff9beccd14bd00f3a93fabcbc1e336f14728 100644 --- a/src/Checker/Checker.py +++ b/src/Checker/Checker.py @@ -92,6 +92,21 @@ class AbstractModelChecker(object): def is_error(self): return self._status == STATUS.ERROR + # Basic check + def basic_check(self, study): + 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 + + return True + # Abstract function def _run(self, study): diff --git a/src/Checker/Study.py b/src/Checker/Study.py index a9562aa25210555e07658c1d1e6e018dc5ba8cd0..1d35973fe8a880526e1ff6317989be7204ca4a02 100644 --- a/src/Checker/Study.py +++ b/src/Checker/Study.py @@ -17,6 +17,8 @@ # -*- coding: utf-8 -*- import time +import logging +from functools import reduce from PyQt5.QtCore import QCoreApplication @@ -25,6 +27,7 @@ from Checker.Checker import AbstractModelChecker, STATUS _translate = QCoreApplication.translate +logger = logging.getLogger() class StudyNetworkReachChecker(AbstractModelChecker): def __init__(self): @@ -37,16 +40,10 @@ class StudyNetworkReachChecker(AbstractModelChecker): self._modules = Modules.NETWORK def run(self, study): - if study is None: - self._status = STATUS.ERROR - self._summary = "invalid_study" + if not self.basic_check(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: @@ -77,16 +74,10 @@ class StudyGeometryChecker(AbstractModelChecker): summary = "ok" status = STATUS.OK - if study is None: - self._status = STATUS.ERROR - self._summary = "invalid_study" + if not self.basic_check(study): return False river = study.river - if river is None: - self._status = STATUS.ERROR - self._summary = "no_river_found" - return False edges = river.enable_edges() if len(edges) == 0: @@ -105,6 +96,118 @@ class StudyGeometryChecker(AbstractModelChecker): self._status = status return ok +class StudyInitialConditionsChecker(AbstractModelChecker): + def __init__(self): + super(StudyInitialConditionsChecker, self).__init__() + + self._name = _translate("Checker", "Study initial conditions checker") + self._description = _translate( + "Checker", "Check initial conditions for each node of study" + ) + self._modules = Modules.INITIAL_CONDITION + + def run(self, study): + ok = True + nerror = 0 + self._summary = "ok" + self._status = STATUS.OK + + if not self.basic_check(study): + return False + + for reach in study.river.enable_edges(): + ok &= self.check_reach(study, reach.reach) + + return ok + + def check_reach(self, study, reach): + ok = True + river = study.river + + if reach not in river.initial_conditions: + self._summary = "missing_initial_condition_defined" + self._status = STATUS.WARNING + return ok + + ic = river.initial_conditions[reach] + len_ic = len(ic) + len_reach = len(reach) + + if len_ic < len_reach: + self._summary = "initial_condition_missing_profile" + self._status = STATUS.WARNING + + if len_ic > len_reach: + self._summary = "more_initial_condition_than_profile" + self._status = STATUS.ERROR + ok = False + + return ok + +class StudyBoundaryConditionChecker(AbstractModelChecker): + def __init__(self): + super(StudyBoundaryConditionChecker, self).__init__() + + self._name = _translate("Checker", "Study boundary conditions checker") + self._description = _translate( + "Checker", "Check boundary conditions for each node of study" + ) + self._modules = Modules.BOUNDARY_CONDITION + + def run(self, study): + ok = True + nerror = 0 + self._summary = "ok" + self._status = STATUS.OK + + if not self.basic_check(study): + return False + + ok = self.check_liquid(study) + + return ok + + def check_liquid(self, study): + river = study.river + + bcs = river.boundary_condition.get_tab('liquid') + if len(bcs) == 0: + self._status = STATUS.ERROR + self._summary = "no_boundary_condition_defined" + return False + + upstream, downstream = reduce( + lambda acc, n: ( + acc[0] + [n] if river.is_upstream_node(n) else acc[0], + acc[1] + [n] if river.is_downstream_node(n) else acc[1], + ), + filter( + lambda n: river.is_enable_node(n), + river.nodes() + ), + ([], []) + ) + + bcs_nodes = set(map(lambda bc: bc.node, bcs)) + + upstream_ok = self.check_liquid_all_node_has_bc(bcs_nodes, upstream) + downstream_ok = self.check_liquid_all_node_has_bc(bcs_nodes, downstream) + + ok = upstream_ok and downstream_ok + + if not ok: + self._status = STATUS.ERROR + self._summary = "no_boundary_condition_at_boundary_node" + + return ok + + def check_liquid_all_node_has_bc(self, bcs_nodes, nodes): + return reduce( + lambda acc, n: (acc and (n in bcs_nodes)), + nodes, + True + ) + class DummyOK(AbstractModelChecker): def __init__(self): diff --git a/src/Model/Study.py b/src/Model/Study.py index fcbedf19d8a5cf8a1360a04a15ff31e86c950d91..c8ce1dd2c7964976223cf018da3b54a419566385 100644 --- a/src/Model/Study.py +++ b/src/Model/Study.py @@ -71,6 +71,8 @@ class Study(SQLModel): lst = [ StudyNetworkReachChecker(), StudyGeometryChecker(), + StudyInitialConditionsChecker(), + StudyBoundaryConditionChecker(), # DummyOK(), # DummyWARNING(), # DummyERROR(), diff --git a/src/Modules.py b/src/Modules.py index 643dd05a8a8ae88a7c5f48be1c2f324c26892eb1..41bf8a735e582c1efb5106c85c12b6b1a869f6fd 100644 --- a/src/Modules.py +++ b/src/Modules.py @@ -23,7 +23,20 @@ from enum import Flag, auto logger = logging.getLogger() -class Modules(Flag): +class IterableFlag(Flag): + def __iter__(self): + all = filter( + lambda v: v in self, + type(self).values() + ) + return all + + @classmethod + def values(cls): + raise Exception("Not implemented yet") + + +class Modules(IterableFlag): NONE = 0 # General @@ -48,6 +61,24 @@ class Modules(Flag): # Display WINDOW_LIST = auto() + @classmethod + def values(cls): + return [ + cls.STUDY, cls.CONFIG, + cls.NETWORK, + cls.GEOMETRY, + cls.BOUNDARY_CONDITION, + cls.LATERAL_CONTRIBUTION, + cls.FRICTION, + cls.INITIAL_CONDITION, + cls.HYDRAULIC_STRUCTURES, + cls.RESERVOIR, + cls.SEDIMENT_LAYER, + cls.ADDITIONAL_FILES, + cls.RESULTS, + cls.WINDOW_LIST, + ] + @classmethod def all(cls): return ~cls.NONE @@ -93,3 +124,40 @@ class Modules(Flag): cls.RESERVOIR: "Reservoir", cls.SEDIMENT_LAYER: "Sediment layer", } + + def impact(self): + res = Modules(0) + for mod in self: + if mod in _impact: + for i in _impact[mod]: + res |= i + return res + + def impact_set(self): + res = [] + for mod in self: + if mod in _impact: + res += _impact[mod] + + return set(res) + +_impact = { + Modules.NETWORK: [ + Modules.GEOMETRY, Modules.BOUNDARY_CONDITION, + Modules.LATERAL_CONTRIBUTION, Modules.FRICTION, + Modules.RESERVOIR, Modules.SEDIMENT_LAYER, + ], + Modules.GEOMETRY: [ + Modules.LATERAL_CONTRIBUTION, Modules.FRICTION, + Modules.INITIAL_CONDITION, Modules.SEDIMENT_LAYER, + ], + Modules.BOUNDARY_CONDITION: [ + Modules.SEDIMENT_LAYER + ], + Modules.LATERAL_CONTRIBUTION: [], + Modules.FRICTION: [], + Modules.INITIAL_CONDITION: [], + Modules.HYDRAULIC_STRUCTURES: [], + Modules.RESERVOIR: [], + Modules.SEDIMENT_LAYER: [], +} diff --git a/src/View/MainWindowTabChecker.py b/src/View/MainWindowTabChecker.py index 09ca5f7989bd6b788333a4a608103ec235548d62..bdc55015f320dbd684f8efcda30ffaa4d59443d9 100644 --- a/src/View/MainWindowTabChecker.py +++ b/src/View/MainWindowTabChecker.py @@ -130,8 +130,9 @@ class WidgetChecker(PamhyrWidget): self._study = study def _checkers_filtered(self, modules): + impacted = modules | modules.impact() return filter( - lambda c: c._modules in modules, + lambda c: c._modules in impacted, self._checkers )