From 9f53ad685e50b01897ceb9a4f9d0fb4fc9a7f783 Mon Sep 17 00:00:00 2001 From: Youcef AOUAD <youcef.aouad@inrae.fr> Date: Fri, 21 Jun 2024 11:39:04 +0200 Subject: [PATCH] d90 Model --- src/Model/D90AdisTS/D90AdisTS.py | 201 ++++++++++++++++++ src/Model/D90AdisTS/D90AdisTSList.py | 62 ++++++ src/Model/D90AdisTS/D90AdisTSSpec.py | 200 +++++++++++++++++ .../InitialConditionsAdisTSList.py | 2 +- .../InitialConditionsAdisTSSpec.py | 2 +- src/Model/River.py | 10 + src/View/MainWindow.py | 15 ++ src/View/ui/MainWindow.ui | 6 + tests_cases/Enlargement/Enlargement.pamhyr | Bin 208896 -> 217088 bytes 9 files changed, 496 insertions(+), 2 deletions(-) create mode 100644 src/Model/D90AdisTS/D90AdisTS.py create mode 100644 src/Model/D90AdisTS/D90AdisTSList.py create mode 100644 src/Model/D90AdisTS/D90AdisTSSpec.py diff --git a/src/Model/D90AdisTS/D90AdisTS.py b/src/Model/D90AdisTS/D90AdisTS.py new file mode 100644 index 00000000..e4953cd8 --- /dev/null +++ b/src/Model/D90AdisTS/D90AdisTS.py @@ -0,0 +1,201 @@ +# D90AdisTS.py -- Pamhyr +# Copyright (C) 2023-2024 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# -*- coding: utf-8 -*- + +import logging +from functools import reduce + +from tools import trace, timer, old_pamhyr_date_to_timestamp + +from Model.Tools.PamhyrDB import SQLSubModel +from Model.Except import NotImplementedMethodeError + +from Model.D90AdisTS.D90AdisTSSpec import D90AdisTSSpec + +logger = logging.getLogger() + +class D90AdisTS(SQLSubModel): + _sub_classes = [ + D90AdisTSSpec, + ] + _id_cnt = 0 + + def __init__(self, id: int = -1, name: str = "default", + status=None): + super(D90AdisTS, self).__init__() + + self._status = status + + if id == -1: + self.id = D90AdisTS._id_cnt + else: + self.id = id + + self._name = name + self._d90 = None + self._enabled = True + self._data = [] + + D90AdisTS._id_cnt = max( + D90AdisTS._id_cnt + 1, + self.id + ) + + @classmethod + def _db_create(cls, execute): + execute(""" + CREATE TABLE d90_adists( + id INTEGER NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + d90 REAL NOT NULL, + enabled BOOLEAN NOT NULL, + ) + """) + + return cls._create_submodel(execute) + + @classmethod + def _db_update(cls, execute, version): + major, minor, release = version.strip().split(".") + if major == minor == "0": + if int(release) < 6: + cls._db_create(execute) + + return True + + @classmethod + def _db_load(cls, execute, data=None): + new = [] + + table = execute( + "SELECT id, name, d90, enabled " + + "FROM d90_adists" + ) + + if table is not None: + for row in table: + d90_id = row[0] + name = row[1] + d90 = row[2] + enabled = (row[3] == 1) + + D90 = cls( + id=d90_id, + name=name, + status=data['status'] + ) + + D90.d90 = d90 + D90.enabled = enabled + + data['d90_default_id'] = d90_id + D90._data = D90AdisTSSpec._db_load(execute, data) + + new.append(D90) + + return new + + def _db_save(self, execute, data=None): + execute(f"DELETE FROM d90_adists WHERE id = {self.id}") + + d90 = -1. + if self.d90 is not None: + d90 = self.d90 + + sql = ( + "INSERT INTO " + + "d90_adists(" + + "id, name, d90, enabled" + + ") " + + "VALUES (" + + f"{self.id}, '{self._db_format(self._name)}', " + + f"{d90}, {self._enabled}" + + ")" + ) + + execute(sql) + + data['d90_default_id'] = self.id + execute( + "DELETE FROM d90_spec " + + f"WHERE d90_default = {self.id}" + ) + + for d90_spec in self._data: + d90_spec._db_save(execute, data) + + return True + + def __len__(self): + return len(self._data) + + @property + def name(self): + return self._name + + @name.setter + def name(self, name): + self._name = name + self._status.modified() + + @property + def d90(self): + return self._d90 + + @d90.setter + def d90(self, d90): + self._d90 = d90 + self._status.modified() + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, enabled): + self._enabled = enabled + self._status.modified() + + def new(self, index): + n = D90AdisTSSpec(status=self._status) + self._data.insert(index, n) + self._status.modified() + return n + + def delete(self, data): + self._data = list( + filter( + lambda x: x not in data, + self._data + ) + ) + self._status.modified() + + def delete_i(self, indexes): + for ind in indexes: + del self._data[ind] + self._status.modified() + + def insert(self, index, data): + self._data.insert(index, data) + self._status.modified() + + + + + + diff --git a/src/Model/D90AdisTS/D90AdisTSList.py b/src/Model/D90AdisTS/D90AdisTSList.py new file mode 100644 index 00000000..a9c8e7f5 --- /dev/null +++ b/src/Model/D90AdisTS/D90AdisTSList.py @@ -0,0 +1,62 @@ +# D90AdisTSList.py -- Pamhyr +# Copyright (C) 2023-2024 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# -*- coding: utf-8 -*- + +from copy import copy +from tools import trace, timer + +from Model.Tools.PamhyrList import PamhyrModelList +from Model.D90AdisTS.D90AdisTS import D90AdisTS + +class D90AdisTSList(PamhyrModelList): + _sub_classes = [ + D90AdisTS, + ] + + @classmethod + def _db_load(cls, execute, data=None): + new = cls(status=data['status']) + + if data is None: + data = {} + + new._lst = D90AdisTS._db_load( + execute, data + ) + + return new + + def _db_save(self, execute, data=None): + execute("DELETE FROM d90_adists") + + if data is None: + data = {} + + for d90 in self._lst: + d90._db_save(execute, data=data) + + return True + + def new(self, index): + n = D90AdisTS(status=self._status) + self._lst.insert(index, n) + self._status.modified() + return n + + + + diff --git a/src/Model/D90AdisTS/D90AdisTSSpec.py b/src/Model/D90AdisTS/D90AdisTSSpec.py new file mode 100644 index 00000000..d1ab2c8d --- /dev/null +++ b/src/Model/D90AdisTS/D90AdisTSSpec.py @@ -0,0 +1,200 @@ +# D90AdisTSSpec.py -- Pamhyr +# Copyright (C) 2023-2024 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# -*- coding: utf-8 -*- + +import logging + +from tools import trace, timer + +from Model.Tools.PamhyrDB import SQLSubModel +from Model.Except import NotImplementedMethodeError + +logger = logging.getLogger() + +class D90AdisTSSpec(SQLSubModel): + _sub_classes = [ + ] + _id_cnt = 0 + + def __init__(self, id: int = -1, name: str = "", + status=None): + super(D90AdisTSSpec, self).__init__() + + self._status = status + + if id == -1: + self.id = D90AdisTSSpec._id_cnt + else: + self.id = id + + self._name_section = name + self._reach = None + self._start_kp = None + self._end_kp = None + self._d90 = None + self._enabled = True + + D90AdisTSSpec._id_cnt = max(D90AdisTSSpec._id_cnt + 1, self.id) + + @classmethod + def _db_create(cls, execute): + execute(""" + CREATE TABLE d90_spec( + id INTEGER NOT NULL PRIMARY KEY, + d90_default INTEGER NOT NULL, + name TEXT NOT NULL, + reach INTEGER NOT NULL, + start_kp REAL NOT NULL, + end_kp REAL NOT NULL, + d90 REAL NOT NULL, + enabled BOOLEAN NOT NULL, + FOREIGN KEY(d90_default) REFERENCES d90_adists(id), + FOREIGN KEY(reach) REFERENCES river_reach(id) + ) + """) + + return cls._create_submodel(execute) + + @classmethod + def _db_update(cls, execute, version): + major, minor, release = version.strip().split(".") + if major == minor == "0": + if int(release) < 6: + cls._db_create(execute) + + return True + + @classmethod + def _db_load(cls, execute, data=None): + new = [] + + table = execute( + "SELECT id, d90_default, name, reach, start_kp, end_kp, " + + "d90, enabled " + + "FROM d90_spec " + + f"WHERE d90_default = {data['d90_default_id']} " + ) + + for row in table: + id = row[0] + name = row[2] + reach = row[3] + start_kp = row[4] + end_kp = row[5] + d90 = row[6] + enabled = (row[7] == 1) + + new_spec = cls( + id=id, + name=name, + status=data['status'] + ) + + new_spec.reach = reach + new_spec.start_kp = start_kp + new_spec.end_kp = end_kp + new_spec.d90 = d90 + new_spec.enabled = enabled + + new.append(new_spec) + + return new + + def _db_save(self, execute, data=None): + d90_default = data['d90_default_id'] + + sql = ( + "INSERT INTO " + + "d90_spec(id, d90_default, name, reach, " + + "start_kp, end_kp, d90, enabled) " + + "VALUES (" + + f"{self.id}, " + + f"{d90_default}, " + + f"'{self._db_format(self._name_section)}', " + + f"{self._reach}, " + + f"{self._start_kp}, " + + f"{self._end_kp}, " + + f"{self._d90}, " + + f"{self._enabled}" + + ")" + ) + execute(sql) + + return True + + @property + def name(self): + return self._name_section + + @name.setter + def name(self, name): + self._name_section = name + self._status.modified() + + @property + def reach(self): + return self._reach + + @reach.setter + def reach(self, reach): + self._reach = reach + self._status.modified() + + @property + def start_kp(self): + return self._start_kp + + @start_kp.setter + def start_kp(self, start_kp): + self._start_kp = start_kp + self._status.modified() + + @property + def end_kp(self): + return self._end_kp + + @end_kp.setter + def end_kp(self, end_kp): + self._end_kp = end_kp + self._status.modified() + + @property + def d90(self): + return self._d90 + + @d90.setter + def d90(self, concentration): + self._d90 = d90 + self._status.modified() + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, enabled): + self._enabled = enabled + self._status.modified() + + + + + + + + + diff --git a/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSList.py b/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSList.py index e45aba04..8ff24cf2 100644 --- a/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSList.py +++ b/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSList.py @@ -41,7 +41,7 @@ class InitialConditionsAdisTSList(PamhyrModelList): return new def _db_save(self, execute, data=None): - execute("DELETE FROM initial_conditions") + execute("DELETE FROM initial_conditions_adists") if data is None: data = {} diff --git a/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSSpec.py b/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSSpec.py index 45286f63..81fad351 100644 --- a/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSSpec.py +++ b/src/Model/InitialConditionsAdisTS/InitialConditionsAdisTSSpec.py @@ -70,7 +70,7 @@ class ICAdisTSSpec(SQLSubModel): ed REAL NOT NULL, rate REAL NOT NULL, enabled BOOLEAN NOT NULL, - FOREIGN KEY(ic_default) REFERENCES initial_conditions(id), + FOREIGN KEY(ic_default) REFERENCES initial_conditions_adists(id), FOREIGN KEY(reach) REFERENCES river_reach(id) ) """) diff --git a/src/Model/River.py b/src/Model/River.py index 5eabb932..4cc2c5e4 100644 --- a/src/Model/River.py +++ b/src/Model/River.py @@ -50,6 +50,7 @@ from Model.Pollutants.PollutantsList import PollutantsList from Model.InitialConditionsAdisTS.InitialConditionsAdisTSList import InitialConditionsAdisTSList from Model.BoundaryConditionsAdisTS.BoundaryConditionsAdisTSList import BoundaryConditionsAdisTSList from Model.LateralContributionsAdisTS.LateralContributionsAdisTSList import LateralContributionsAdisTSList +from Model.D90AdisTS.D90AdisTSList import D90AdisTSList class RiverNode(Node, SQLSubModel): @@ -239,6 +240,7 @@ class River(Graph, SQLSubModel): InitialConditionsAdisTSList, BoundaryConditionsAdisTSList, LateralContributionsAdisTSList, + D90AdisTSList, ] def __init__(self, status=None): @@ -267,6 +269,7 @@ class River(Graph, SQLSubModel): self._InitialConditionsAdisTS = InitialConditionsAdisTSList(status=self._status) self._BoundaryConditionsAdisTS = BoundaryConditionsAdisTSList(status=self._status) self._LateralContributionsAdisTS = LateralContributionsAdisTSList(status=self._status) + self._D90AdisTS = D90AdisTSList(status=self._status) @classmethod def _db_create(cls, execute): @@ -353,6 +356,8 @@ class River(Graph, SQLSubModel): new._LateralContributionsAdisTS = LateralContributionsAdisTSList._db_load(execute, data) + new._D90AdisTS = D90AdisTSList._db_load(execute, data) + return new def _db_save(self, execute, data=None): @@ -377,6 +382,7 @@ class River(Graph, SQLSubModel): objs.append(self._InitialConditionsAdisTS) objs.append(self._BoundaryConditionsAdisTS) objs.append(self._LateralContributionsAdisTS) + objs.append(self._D90AdisTS) self._save_submodel(execute, objs, data) return True @@ -520,6 +526,10 @@ Last export at: @date.""" def lateral_contributions_adists(self): return self._LateralContributionsAdisTS + @property + def d90_adists(self): + return self._D90AdisTS + def get_params(self, solver): if solver in self._parameters: return self._parameters[solver] diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py index f94c4d07..12d9af67 100644 --- a/src/View/MainWindow.py +++ b/src/View/MainWindow.py @@ -122,6 +122,7 @@ define_model_action = [ "action_menu_boundary_conditions_sediment", "action_menu_rep_additional_lines", "action_menu_output_kp", "action_menu_run_adists", "action_menu_pollutants", + "action_menu_d90", ] action = ( @@ -235,6 +236,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): """ actions = { # Menu action + "action_menu_d90": self.open_d90, "action_menu_pollutants": self.open_pollutants, "action_menu_run_adists":self.run_solver_adists, "action_menu_output_kp": self.open_output_kp_adists, @@ -866,6 +868,19 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): # SUB WINDOWS # ############### + def open_d90(self): + if self.sub_window_exists( + PollutantsWindow, + data=[self._study, None] + ): + return + + Pollutants = PollutantsWindow( + study=self._study, + parent=self + ) + Pollutants.show() + def open_pollutants(self): if self.sub_window_exists( PollutantsWindow, diff --git a/src/View/ui/MainWindow.ui b/src/View/ui/MainWindow.ui index 20ec6283..842e220a 100644 --- a/src/View/ui/MainWindow.ui +++ b/src/View/ui/MainWindow.ui @@ -219,6 +219,7 @@ </property> <addaction name="action_menu_output_kp"/> <addaction name="action_menu_pollutants"/> + <addaction name="action_menu_d90"/> </widget> <addaction name="menu_File"/> <addaction name="menu_network"/> @@ -761,6 +762,11 @@ <string>Pollutants</string> </property> </action> + <action name="action_menu_d90"> + <property name="text"> + <string>D90</string> + </property> + </action> </widget> <resources/> <connections> diff --git a/tests_cases/Enlargement/Enlargement.pamhyr b/tests_cases/Enlargement/Enlargement.pamhyr index 7cbb7e4ffefc175307429c2d8af87987164093b8..9aa6c32eaddadc52309022f8de7ef26e5bb9a78a 100644 GIT binary patch delta 3021 zcmaKuO>EOv9Kh{3b`og%nx(8Ouj$8HCn?b2G)>!37<7yc#`nfH*jMJ-PHP$`ne9ep z7t;%uX%eUhE)(iur=12BaGE$x)5JFIItig2VC*tY8sfxde}6p3G|?n0lJon$|9kKM z{(kRW{-f*iM*Qj!UA#HGIMg*eo6qNS)06p`QfYI2$T-urOrgKfeRLaLL(4bUE+BS{ zWlqxTpIgH;w|>nU9bkGHW`Z{A)uy7C8=@|1imB)t4l#4|^|fE<zRf3A|J81mq9__Y zhhM6Qo^K|;`v)=9lyYU5zB3#O_iSU(0}4GuPtb$Sgzzisi!m&Pl2M_M6S_hywIdlV zObdc=_vyh<qNj&Lw<z=!{fT}@Kcjo-7PPs6=C^Y!m4r4q0iNxw^HRta?3ZwmK<JhR z!Tp!JPs^eBZ;?0*QlXw*kMz)It=MYpbSxJOM;}J-MK_{rQ7L*jIud0fk0U=tHX^H$ zQ;}RG%01+6bJw|5?mRcg4RSR5i2afMmi>~gvd7sR+Z}!s{yw}Oz8F3f?q~jG?la#o zUohvGJxn<CAapzQRY(aP2=&r`(?8H((>i^W9-^MvxuULJ8MLwrN5#hZlvu5nD{4)w zmQ7VtN~O|1cmx3+QC1Dps7e``qoNttRBNi~h>$kd3W!;Wqv*LL-lwlDi28z3HWV3R z*37ajEs44TIrB(%!E)77XH{;g84=6_2+y{eO;MNXH6!nV^k;TRglF1BS*g@(GV%B_ zcqI$r=?<c%u97ikJ_T=P2QId03!1tj<Yoy-f8usL)h0?}18SIboc`GL3btpxke_<P zRO_`#FF^VuS0wMPJW5M-byXt&^B;m&LYQ>W@?@n^H;I<M;D#d;b2Q6Y7xPjBlH&{r z#*;qpihJ88vQkrn6ZydPa&2DSS!mMH*!$qkAUx6TH7wI0`Om&*Ykhc6o7Pkf2`XWV zEGd7^?l_Kjx0%{X)hv?*;oo(&U7a;&o$UPc@3`K0XOE<XKkI7Q&KgN`_KdA1ai+5d zTOi3x<}^oHbSa6`ZR(O(t%xl%d&-T;w~0=o7h9hRe-g}M5|6c+jk+%6NNr|M*jhgx zwc0$rZcI)WTM-A3gPKX=S6k+y78o0T8~8{Pj}R_vLXMQ;&@q6c{dkzbt<kO2Q8304 z9wG*O1C@qEa+G?@#e)Q!x(M6JCGQo#37AJX<>N|ux<=p;7Y+nq(Qo;N11*dLbjqiP z-KIMOu;9bjUDzLhlRlhx;VS`{Cvfl(K*(huffY+u77BU6pYB0hNZ^-!0r+H|;DOg{ zjPQ;i&XZ(k4%n2(y@WOlu}QXaXE*mic0Y*YJWlE>mb*jCrXj90y<I=3j?IBO!sC}n zOw;IK4eqn8ew-kdY!={T@GCuAay#rH9JaZJY=UC>r-z5qv!L=kM!u?vs~x(`%zy`z z?e;zUObQ-H9@sez;wZv#BI-(mRLDTlK7SygT?DFi*lc_}<<jjf3Zo{AIa~mmkK=7E zYM6CtSzJ`Se+DPPn26(8%Yfyq>$3MgkmoF?`)E*TlI~LiG>1<kLa44R!Z}KcA+^`V z9Kmo3%3_mjSE-zf*%z=zie+p9@aU+8!#=N6g<KIbC>+`C7i4x1X!CK**!Kgeu4!UT zE?3o>QWo{a75nfoy!J!8!Q}gJ$TsaegkdTTr~<zTGP|6c27ckDv7gkZbC2*srN^C{ zhGTZTX?Wh<jec6@dZXQUDD)6*qBV3LO`@*u$K7{suHERiCNvn*kwhxyUSZ01EER4z zkSGa8u8a0*JMCX^_$uhdL@Mg(WD;c!mb9gfErV7_q#|22$!G?wi}u=r#%<LW)IgsZ z6@rx#DR!%6NV=i~L}3ZUW64z56IHEIRVCGQ=Fqwu!wR}WRT@9B2u<eWssC9!;tp3p M<@-|Yi${$A0MPiNaR2}S delta 176 zcmV;h08jsbpbdcF43HlIGqE6N9}G7)FfukSH#9UjFgLT4A9GL)Yyc1F56cg=50MXS zv4L6-1tSFmO$n25e;|{Qe<K_M6axb-0dsF`c4cy3aA9&`ZDn+2awr=D13Lkifz1II zv*&*qkOvI_001ZnpqB?Q15vSoNC~%~IRj({hXf!2rvxAZ5C{Sf>HrV*59+fKK;91t e2?7NG4-*J5Gq>|U0ud0m1Rw$M54ZGR0u0d98#4s} -- GitLab