diff --git a/src/Meshing/Mage.py b/src/Meshing/Mage.py index ce408ac0..fb591292 100644 --- a/src/Meshing/Mage.py +++ b/src/Meshing/Mage.py @@ -381,8 +381,6 @@ class MeshingWithMageMailleurTT(AMeshingTool): st_file = self.export_reach_to_st(reach, tmp) m_file = st_file.rsplit(".ST", 1)[0] + ".M" - os.sync() - proc = QProcess() proc.setWorkingDirectory(tmp) @@ -397,12 +395,13 @@ class MeshingWithMageMailleurTT(AMeshingTool): logger.info( f"! {self._exe_path()} " + f"{st_file} {m_file} " + - f"update_rk " + + f"update_kp " + f"{str(step)} " + f"{limites[0]} {limites[1]} " + f"{directrices[0]} {directrices[1]} " + f"{orientation} {lm} {linear} " + - f"{origin} " + f"{origin} " + + f"{origin_value} " ) proc.start( self._exe_path(), diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py index a56b9e31..b28c6b5f 100644 --- a/src/Model/InitialConditions/InitialConditions.py +++ b/src/Model/InitialConditions/InitialConditions.py @@ -21,6 +21,7 @@ import logging from copy import copy, deepcopy from tools import trace, timer from functools import reduce +from numpy import interp from Model.Tools.PamhyrDB import SQLSubModel from Model.Scenario import Scenario @@ -485,8 +486,23 @@ class InitialConditions(SQLSubModel): def get_discharge(self): return self._data_get("discharge") - def generate_growing_constante_height(self, height: float, - compute_discharge: bool): + def _sort_by_z_and_rk(self, profiles): + profiles.sort( + reverse=False, + key=lambda p: p.rk + ) + + first_z = profiles[0].z() + last_z = profiles[-1].z() + + if first_z > last_z: + profiles.sort( + reverse=True, + key=lambda p: p.rk + ) + + def generate_growing_constant_depth(self, height: float, + compute_discharge: bool): profiles = self._reach.reach.profiles.copy() previous_elevation = -99999.99 diff --git a/src/Model/Network/Edge.py b/src/Model/Network/Edge.py index 52708f3e..e96630ae 100644 --- a/src/Model/Network/Edge.py +++ b/src/Model/Network/Edge.py @@ -41,9 +41,9 @@ class Edge(SQLSubModel): elif name == "id": ret = self.pamhyr_id elif name == "node1": - ret = self.node1.name + ret = self.node1 elif name == "node2": - ret = self.node2.name + ret = self.node2 elif name == "enable": ret = self._enable @@ -69,10 +69,7 @@ class Edge(SQLSubModel): @property def name(self): - name = self._name - if self._name == "": - name = f"{self.node1.name} -> {self.node2.name}" - return name + return self._name def is_enable(self): return self._enable diff --git a/src/Model/Network/Node.py b/src/Model/Network/Node.py index ccd6a2a1..f08858dc 100644 --- a/src/Model/Network/Node.py +++ b/src/Model/Network/Node.py @@ -26,11 +26,7 @@ class Node(SQLSubModel): **kwargs): super(Node, self).__init__(id=id, **kwargs) - if name == "": - self._name = f"Node #{self.pamhyr_id}" - else: - self._name = name - + self._name = name self.pos = Point(x, y) def __getitem__(self, key): diff --git a/src/Model/Tools/PamhyrDict.py b/src/Model/Tools/PamhyrDict.py index 519093c2..47508439 100644 --- a/src/Model/Tools/PamhyrDict.py +++ b/src/Model/Tools/PamhyrDict.py @@ -73,7 +73,7 @@ class PamhyrModelDict(SQLSubModel): if key in self._dict: v = self._dict[key] - if type(v) is types.GeneratorType: + if isinstance(v, types.GeneratorType): return list(v) return v diff --git a/src/Solver/RubarBE.py b/src/Solver/RubarBE.py index 5c9f7909..bf946f42 100644 --- a/src/Solver/RubarBE.py +++ b/src/Solver/RubarBE.py @@ -30,13 +30,13 @@ from Model.Results.River.River import River, Reach, Profile logger = logging.getLogger() -class RubarBE(CommandLineSolver): - _type = "rubarbe" +class Rubar3(CommandLineSolver): + _type = "rubar3" def __init__(self, name): - super(RubarBE, self).__init__(name) + super(Rubar3, self).__init__(name) - self._type = "rubarbe" + self._type = "rubar3" self._cmd_input = "" self._cmd_solver = "@path @input -o @output" @@ -44,9 +44,9 @@ class RubarBE(CommandLineSolver): @classmethod def default_parameters(cls): - lst = super(RubarBE, cls).default_parameters() + # lst = super(Rubar3, cls).default_parameters() - lst += [ + lst = [ ("rubarbe_cfl", "0.50000E+00"), ("rubarbe_condam", "1"), ("rubarbe_condav", "3"), @@ -60,10 +60,10 @@ class RubarBE(CommandLineSolver): ("rubarbe_tinit", "000:00:00:00"), ("rubarbe_tmax", "999:99:99:00"), ("rubarbe_tiopdt", "000:00:00:00"), - ("rubarbe_dt", "3000.0"), + ("rubarbe_dt", "5.0"), ("rubarbe_ts", "999:99:99:00"), - ("rubarbe_dtsauv", "999:99:99:00"), - ("rubarbe_psave", "999:99:99:00"), + ("rubarbe_dtsauv", "00:00:00:05"), + ("rubarbe_psave", "00:00:00:05"), ("rubarbe_fdeb1", "1"), ("rubarbe_fdeb2", "10"), ("rubarbe_fdeb3", "100"), @@ -119,21 +119,21 @@ class RubarBE(CommandLineSolver): ########## def cmd_args(self, study): - lst = super(RubarBE, self).cmd_args(study) + lst = super(Rubar3, self).cmd_args(study) return lst def input_param(self): name = self._study.name - return f"{name}.REP" + return f"{name}" def output_param(self): name = self._study.name - return f"{name}.BIN" + return f"{name}" def log_file(self): name = self._study.name - return f"{name}.TRA" + return f"{name}" def export(self, study, repertory, qlog=None): self._study = study @@ -143,8 +143,12 @@ class RubarBE(CommandLineSolver): self._export_ts(study, repertory, qlog, name=name) self._export_geomac_i(study, repertory, qlog, name=name) self._export_mail(study, repertory, qlog, name=name) - self._export_tps(study, repertory, qlog, name=name) + self._export_condin(study, repertory, qlog, name=name) self._export_stricklers(study, repertory, qlog, name=name) + self._export_hydro(study, repertory, qlog, name=name) + self._export_condav(study, repertory, qlog, name=name) + + return True def _export_donnee(self, study, repertory, qlog, name="0"): if qlog is not None: @@ -168,6 +172,9 @@ class RubarBE(CommandLineSolver): name = param.name value = param.value + if "all_" in name: + continue + if value != "": # Value format if value.count(':') == 3: @@ -276,7 +283,9 @@ class RubarBE(CommandLineSolver): if label[0] == "r": label = label[1].upper() else: - label = lable[0] + label = label[1].upper() + else: + label = " " y = point.y z = point.z @@ -361,13 +370,13 @@ class RubarBE(CommandLineSolver): ind += 1 f.write("\n") - def _export_tps(self, study, repertory, qlog, name="0"): + def _export_condin(self, study, repertory, qlog, name="0"): if qlog is not None: - qlog.put("Export TPS file") + qlog.put("Export CONDIN file") with open( os.path.join( - repertory, f"tps.{name}" + repertory, f"condin.{name}" ), "w+" ) as f: for edge in study.river.enable_edges(): @@ -376,7 +385,7 @@ class RubarBE(CommandLineSolver): f.write(f"0.0\n") ics = study.river.initial_conditions.get(edge) - data = self._export_tps_init_data(ics) + data = self._export_condin_init_data(ics) profiles = reach.profiles first = profiles[0] @@ -388,11 +397,11 @@ class RubarBE(CommandLineSolver): ) return - f_h_s = self._export_tps_profile_height_speed(first, data) - l_h_s = self._export_tps_profile_height_speed(last, data) + f_h_s = self._export_condin_profile_height_speed(first, data) + l_h_s = self._export_condin_profile_height_speed(last, data) # First mail - f.write(f"{1:>5} {f_h_s[0]} {f_h_s[1]}") + f.write(f"{1:>5} {f_h_s[0]} {f_h_s[1]}\n") ind = 2 it = iter(profiles) @@ -404,7 +413,7 @@ class RubarBE(CommandLineSolver): ind += 1 continue - cur_h, cur_s = self._export_tps_profile_height_speed( + cur_h, cur_s = self._export_condin_profile_height_speed( profile, data ) @@ -418,9 +427,9 @@ class RubarBE(CommandLineSolver): ind += 1 # Last mail - f.write(f"{ind:>5} {f_h_s[0]} {f_h_s[1]}") + f.write(f"{ind:>5} {f_h_s[0]} {f_h_s[1]}\n") - def _export_tps_init_data(self, ics): + def _export_condin_init_data(self, ics): data = {} for d in ics.data: @@ -431,11 +440,70 @@ class RubarBE(CommandLineSolver): return data - def _export_tps_profile_height_speed(self, profile, data): + def _export_condin_profile_height_speed(self, profile, data): z = data[profile.rk][0] q = data[profile.rk][1] - height = z - profile.z_min() + # height = z - profile.z_min() speed = profile.speed(q, z) - return height, speed + return z, speed + + def _export_hydro(self, study, repertory, qlog, name="0"): + if qlog is not None: + qlog.put("Export HYDRO file") + + with open( + os.path.join( + repertory, f"hydro.{name}" + ), "w+" + ) as f: + bcs = [] + for edge in study.river.enable_edges(): + for bound in study.river.boundary_condition.get_tab("liquid"): + # BC is an hydrogramme + if bound.bctype == "TD" or bound.bctype == "PC": + # BC is on input node of this reach + if bound.node == edge.node1: + bcs.append(bound) + + for bc in bcs: + f.write(f"{len(bc)}\n") + for d0, d1 in bc.data: + f.write(f"{d0} {d1}\n") + + def _export_condav(self, study, repertory, qlog, name="0"): + if qlog is not None: + qlog.put("Export CONDAV file") + + with open( + os.path.join( + repertory, f"condav.{name}" + ), "w+" + ) as f: + bcs = [] + for edge in study.river.enable_edges(): + for bound in study.river.boundary_condition.get_tab("liquid"): + # BC is an hydrogramme + if bound.bctype == "ZD" or bound.bctype == "TZ": + # BC is on input node of this reach + if bound.node == edge.node2: + bcs.append(bound) + + for bc in bcs: + f.write(f"{len(bc)}\n") + for d0, d1 in bc.data: + f.write(f"{d0} {d1}\n") + + +class RubarBE(Rubar3): + _type = "rubarbe" + + def __init__(self, name): + super(RubarBE, self).__init__(name) + + self._type = "rubarbe" + + self._cmd_input = "" + self._cmd_solver = "@path @input -o @output" + self._cmd_output = "" diff --git a/src/Solver/Solvers.py b/src/Solver/Solvers.py index cd336e68..d0a8a981 100644 --- a/src/Solver/Solvers.py +++ b/src/Solver/Solvers.py @@ -22,7 +22,7 @@ from Solver.GenericSolver import GenericSolver from Solver.Mage import ( Mage7, Mage8, MageFake7, ) -from Solver.RubarBE import RubarBE +from Solver.RubarBE import Rubar3, RubarBE _translate = QCoreApplication.translate @@ -32,6 +32,7 @@ solver_long_name = { "mage8": "Mage v8", # "mage_fake7": "Mage fake v7", # "rubarbe": "RubarBE", + # "rubar3": "Rubar3", } solver_type_list = { @@ -40,4 +41,5 @@ solver_type_list = { "mage8": Mage8, # "mage_fake7": MageFake7, # "rubarbe": RubarBE, + # "rubar3": Rubar3, } diff --git a/src/View/About/Window.py b/src/View/About/Window.py index 24931a08..aa6eab08 100644 --- a/src/View/About/Window.py +++ b/src/View/About/Window.py @@ -67,5 +67,5 @@ class AboutWindow(PamhyrDialog): label = f"\n - {author}" + label except StopIteration: label = _translate("About", "Contributors: ") + label - label = "Copyright © 2022-2024 INRAE\n" + label + # label = "Copyright © 2022-2024 INRAE\n" + label self.set_label_text("label_copyright", label) diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index ed9da7a3..fb3a2738 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -133,6 +133,7 @@ class EditBoundaryConditionWindow(PamhyrWindow): self.setup_plot() self.setup_data() self.setup_connections() + self.setup_dialog() def setup_data(self): self._is_solid = self._data.bctype == "SL" @@ -208,13 +209,17 @@ class EditBoundaryConditionWindow(PamhyrWindow): .triggered.connect(self.generate_uniform) self.find(QAction, "action_generate_critical")\ .triggered.connect(self.generate_critical) + self.find(QAction, "action_increasing")\ + .triggered.connect(self.make_increasing) if self._data.bctype != "ZD" or not self._data.has_node: self.find(QAction, "action_generate_uniform").setVisible(False) self.find(QAction, "action_generate_critical").setVisible(False) + self.find(QAction, "action_increasing").setVisible(False) else: self.find(QAction, "action_generate_uniform").setVisible(True) self.find(QAction, "action_generate_critical").setVisible(True) + self.find(QAction, "action_increasing").setVisible(True) self._table.dataChanged.connect(self.update) self._table.layoutChanged.connect(self.update) @@ -226,6 +231,10 @@ class EditBoundaryConditionWindow(PamhyrWindow): else: self._d50sigma.setEnabled(False) + def setup_dialog(self): + reach = self._data.reach(self._study.river)[0] + self.slope_value = abs(reach.get_incline_median_mean()) + def d50_changed(self, value): self._undo_stack.push( SetMetaDataCommand( @@ -340,15 +349,16 @@ class EditBoundaryConditionWindow(PamhyrWindow): def generate_uniform(self): if self._data.has_node: node = self._data.node + if node is None: + return reach = self._data.reach(self._study.river)[0] profile = reach.profiles[-1] - incline = abs(reach.get_incline_median_mean()) - dlg = GenerateDialog(incline, + dlg = GenerateDialog(self.slope_value, reach, trad=self._trad, parent=self) if dlg.exec(): - incline = dlg.value + self.slope_value = dlg.value frictions = reach._parent.frictions.frictions z_min = profile.z_min() z_max = profile.z_max() @@ -360,8 +370,10 @@ class EditBoundaryConditionWindow(PamhyrWindow): strickler = 25.0 height = [(i)*(z_max-z_min)/50 for i in range(51)] q = [((profile.wet_width(z_min + h) * 0.8) * strickler - * (h ** (5/3)) * (abs(incline) ** (0.5))) + * (h ** (5/3)) * (abs(self.slope_value) ** (0.5))) for h in height] + for i in range(len(height)): + height[i] += z_min self._table.replace_data(height, q) return @@ -369,6 +381,8 @@ class EditBoundaryConditionWindow(PamhyrWindow): def generate_critical(self): if self._data.has_node: node = self._data.node + if node is None: + return reach = self._data.reach(self._study.river)[0] profile = reach.profiles[-1] z_min = profile.z_min() @@ -377,5 +391,25 @@ class EditBoundaryConditionWindow(PamhyrWindow): q = [sqrt(9.81 * (profile.wet_area(z_min + h) ** 3) / profile.wet_width(z_min + h)) for h in height] + for i in range(len(height)): + height[i] += z_min self._table.replace_data(height, q) return + + def make_increasing(self): + if self._data.has_node: + node = self._data.node + if node is None: + return + if len(self._table._data) < 2: + return + h = [self._data.get_i(0)[0]] + q = [self._data.get_i(0)[1]] + for i in range(len(self._table._data)): + if i == 0: + continue + row = self._data.get_i(i) + if row[1] > q[-1]: + h.append(row[0]) + q.append(row[1]) + self._table.replace_data(h, q) diff --git a/src/View/Configure/Solver/Window.py b/src/View/Configure/Solver/Window.py index d3551ba4..6293832f 100644 --- a/src/View/Configure/Solver/Window.py +++ b/src/View/Configure/Solver/Window.py @@ -74,17 +74,17 @@ class ConfigureSolverWindow(PamhyrDialog): # File button buttons = { "pushButton_input": (lambda: self.file_dialog( - select_file=True, + select_file="ExistingFile", callback=lambda f: self.set_line_edit_text( "lineEdit_input", f[0]) )), "pushButton_solver": (lambda: self.file_dialog( - select_file=True, + select_file="ExistingFile", callback=lambda f: self.set_line_edit_text( "lineEdit_solver", f[0]) )), "pushButton_output": (lambda: self.file_dialog( - select_file=True, + select_file="ExistingFile", callback=lambda f: self.set_line_edit_text( "lineEdit_output", f[0]) )), diff --git a/src/View/Configure/Window.py b/src/View/Configure/Window.py index 60f1dd67..1ed011ca 100644 --- a/src/View/Configure/Window.py +++ b/src/View/Configure/Window.py @@ -157,7 +157,7 @@ class ConfigureWindow(PamhyrDialog): "pushButton_stricklers_sort": self.sort_stricklers, # Others # "pushButton_backup_path": lambda: self.file_dialog( - # select_file=False, + # select_file="Directory", # callback=lambda f: self.set_line_edit_text( # "lineEdit_backup_path", f[0] # ) diff --git a/src/View/Geometry/PlotAC.py b/src/View/Geometry/PlotAC.py index ad43fdf3..2be00355 100644 --- a/src/View/Geometry/PlotAC.py +++ b/src/View/Geometry/PlotAC.py @@ -41,7 +41,7 @@ class PlotAC(PamhyrPlot): self._autoscale_update = True self.label_x = self._trad["transverse_abscissa"] - self.label_y = self._trad["unit_height"] + self.label_y = self._trad["unit_elevation"] self.label_previous_plot_selected = self._trad["prev_cs"] self.label_plot_selected = self._trad["cs"] diff --git a/src/View/Geometry/Profile/Plot.py b/src/View/Geometry/Profile/Plot.py index 0eb1543f..f8c790f6 100644 --- a/src/View/Geometry/Profile/Plot.py +++ b/src/View/Geometry/Profile/Plot.py @@ -56,8 +56,8 @@ class Plot(PamhyrPlot): self.line_xy = [] self.line_gl = [] - self.label_x = self._trad["unit_rk"] - self.label_y = self._trad["unit_height"] + self.label_x = self._trad["transverse_abscissa"] + self.label_y = self._trad["unit_elevation"] self.before_plot_selected = None self.plot_selected = None diff --git a/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py b/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py index 29a12f35..9d872d24 100644 --- a/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py +++ b/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py @@ -108,7 +108,7 @@ hydraulic structure values?" self._sub_dict["long_types"] = { "ND": self._dict["not_defined"], "S1": _translate( - "BasicHydraulicStructures", "Discharge weir" + "BasicHydraulicStructures", "Rectangular weir" ), "S2": _translate( "BasicHydraulicStructures", "Trapezoidal weir" diff --git a/src/View/InitialConditions/DialogDepth.py b/src/View/InitialConditions/DialogDepth.py new file mode 100644 index 00000000..53abfe39 --- /dev/null +++ b/src/View/InitialConditions/DialogDepth.py @@ -0,0 +1,60 @@ +# DialogDepth.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 . + +# -*- coding: utf-8 -*- + +from View.Tools.PamhyrWindow import PamhyrDialog + +from PyQt5.QtGui import ( + QKeySequence, +) + +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, +) + +from PyQt5.QtWidgets import ( + QDialogButtonBox, QComboBox, QUndoStack, QShortcut, + QDoubleSpinBox, QCheckBox, +) + + +class DepthDialog(PamhyrDialog): + _pamhyr_ui = "InitialConditions_Dialog_Generator_Depth" + _pamhyr_name = "Depth" + + def __init__(self, value, option, trad=None, parent=None): + super(DepthDialog, self).__init__( + title=trad[self._pamhyr_name], + options=[], + trad=trad, + parent=parent + ) + + self.value = value + self.option = option + self.sb = self.find(QDoubleSpinBox, "doubleSpinBox") + self.sb.setValue(self.value) + self.cb = self.find(QCheckBox, "checkBox") + self.cb.setChecked(self.option) + + def accept(self): + self.value = self.sb.value() + self.option = self.cb.isChecked() + super().accept() + + def reject(self): + self.close() diff --git a/src/View/InitialConditions/DialogDischarge.py b/src/View/InitialConditions/DialogDischarge.py index 3f0a83bb..c7640037 100644 --- a/src/View/InitialConditions/DialogDischarge.py +++ b/src/View/InitialConditions/DialogDischarge.py @@ -36,7 +36,12 @@ class DischargeDialog(PamhyrDialog): _pamhyr_ui = "InitialConditions_Dialog_Generator_Discharge" _pamhyr_name = "Discharge" - def __init__(self, title="Discharge", trad=None, parent=None): + def __init__(self, + value, + option, + title="Discharge", + trad=None, + parent=None): super(DischargeDialog, self).__init__( title=trad[self._pamhyr_name], options=[], @@ -44,12 +49,16 @@ class DischargeDialog(PamhyrDialog): parent=parent ) - self.value = None - self.option = None + self.value = value + self.option = option + self.sb = self.find(QDoubleSpinBox, "doubleSpinBox") + self.sb.setValue(self.value) + self.cb = self.find(QCheckBox, "checkBox") + self.cb.setChecked(self.option) def accept(self): - self.value = self.find(QDoubleSpinBox, "doubleSpinBox").value() - self.option = self.find(QCheckBox, "checkBox").isChecked() + self.value = self.sb.value() + self.option = self.cb.isChecked() super().accept() def reject(self): diff --git a/src/View/InitialConditions/DialogHeight.py b/src/View/InitialConditions/DialogHeight.py index d4e541aa..0f9814c3 100644 --- a/src/View/InitialConditions/DialogHeight.py +++ b/src/View/InitialConditions/DialogHeight.py @@ -28,7 +28,7 @@ from PyQt5.QtCore import ( from PyQt5.QtWidgets import ( QDialogButtonBox, QComboBox, QUndoStack, QShortcut, - QDoubleSpinBox, QCheckBox, + QDoubleSpinBox, QCheckBox, QLabel ) @@ -36,7 +36,7 @@ class HeightDialog(PamhyrDialog): _pamhyr_ui = "InitialConditions_Dialog_Generator_Height" _pamhyr_name = "Height" - def __init__(self, trad=None, parent=None): + def __init__(self, values, option, trad=None, parent=None): super(HeightDialog, self).__init__( title=trad[self._pamhyr_name], options=[], @@ -44,12 +44,29 @@ class HeightDialog(PamhyrDialog): parent=parent ) - self.value = None - self.option = None + self.values = values + self.option = option + self.sb1 = self.find(QDoubleSpinBox, "doubleSpinBox_1") + self.sb1.setValue(self.values[0]) + self.sb2 = self.find(QDoubleSpinBox, "doubleSpinBox_2") + self.sb2.setValue(self.values[1]) + self.sb3 = self.find(QDoubleSpinBox, "doubleSpinBox_3") + self.sb3.setValue(self.values[2]) + self.cb = self.find(QCheckBox, "checkBox") + self.cb.setChecked(self.option) + self.enable_discharge() + self.cb.clicked.connect(self.enable_discharge) + + def enable_discharge(self): + label = self.find(QLabel, "label_3") + self.sb3.setEnabled(self.cb.isChecked()) + label.setEnabled(self.cb.isChecked()) def accept(self): - self.value = self.find(QDoubleSpinBox, "doubleSpinBox").value() - self.option = self.find(QCheckBox, "checkBox").isChecked() + self.values[0] = self.sb1.value() + self.values[1] = self.sb2.value() + self.values[2] = self.sb3.value() + self.option = self.cb.isChecked() super().accept() def reject(self): diff --git a/src/View/InitialConditions/UndoCommand.py b/src/View/InitialConditions/UndoCommand.py index bcc1d8a5..15c35611 100644 --- a/src/View/InitialConditions/UndoCommand.py +++ b/src/View/InitialConditions/UndoCommand.py @@ -195,8 +195,13 @@ class GenerateCommand(QUndoCommand): def redo(self): if self._generator == "growing": - self._ics.generate_growing_constante_height(self._param, - self._option) + self._ics.generate_growing_constant_depth(self._param, + self._option) elif self._generator == "discharge": self._ics.generate_discharge(self._param, self._option) + elif self._generator == "height": + self._ics.generate_height(self._param[0], + self._param[1], + self._option, + self._param[2]) diff --git a/src/View/InitialConditions/Window.py b/src/View/InitialConditions/Window.py index 2bc617bd..0947b280 100644 --- a/src/View/InitialConditions/Window.py +++ b/src/View/InitialConditions/Window.py @@ -58,6 +58,7 @@ from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar from View.InitialConditions.PlotDRK import PlotDRK from View.InitialConditions.PlotDischarge import PlotDischarge from View.InitialConditions.translate import ICTranslate +from View.InitialConditions.DialogDepth import DepthDialog from View.InitialConditions.DialogHeight import HeightDialog from View.InitialConditions.DialogDischarge import DischargeDialog from View.WaitingDialog import WaitingDialog @@ -103,6 +104,7 @@ class InitialConditionsWindow(PamhyrWindow): self.setup_table() self.setup_plot() self.setup_connections() + self.setub_dialogs() self.ui.setWindowTitle(self._title) @@ -179,16 +181,27 @@ class InitialConditionsWindow(PamhyrWindow): self.find(QAction, "action_import").triggered\ .connect(self.import_from_file) - self.find(QPushButton, "pushButton_generate_1").clicked.connect( - self.generate_growing_constante_height - ) + self.find(QPushButton, "pushButton_generate_1").clicked.connect( + self.generate_growing_constant_depth + ) self.find(QPushButton, "pushButton_generate_2").clicked.connect( self.generate_discharge ) + self.find(QPushButton, "pushButton_generate_3").clicked.connect( + self.generate_height + ) self._table.dataChanged.connect(self._update_plot) + def setub_dialogs(self): + self.height_values = [0.0, 0.0, 0.0] + self.height_option = True + self.discharge_value = 0.0 + self.discharge_option = True + self.depth_value = 0.0 + self.depth_option = True + def index_selected_row(self): table = self.find(QTableView, f"tableView") rows = table.selectionModel()\ @@ -255,6 +268,7 @@ class InitialConditionsWindow(PamhyrWindow): workdir = os.path.dirname(self._study.filename) return self.file_dialog( + select_file="ExistingFile", callback=lambda d: self._import_from_file(d[0]), directory=workdir, default_suffix=".BIN", @@ -352,18 +366,41 @@ class InitialConditionsWindow(PamhyrWindow): self._table.redo() self._update() - def generate_growing_constante_height(self): - dlg = HeightDialog(trad=self._trad, parent=self) + def generate_growing_constant_depth(self): + dlg = DepthDialog(self.depth_value, + self.depth_option, + trad=self._trad, + parent=self) if dlg.exec(): - value = dlg.value - compute_discharge = dlg.option - self._table.generate("growing", value, compute_discharge) + self.depth_value = dlg.value + self.depth_option = dlg.option + self._table.generate("growing", + self.depth_value, + self.depth_option) self._update() def generate_discharge(self): - dlg = DischargeDialog(trad=self._trad, parent=self) + dlg = DischargeDialog(self.discharge_value, + self.discharge_option, + trad=self._trad, + parent=self) if dlg.exec(): - value = dlg.value - compute_height = dlg.option - self._table.generate("discharge", value, compute_height) + self.discharge_value = dlg.value + self.discharge_option = dlg.option + self._table.generate("discharge", + self.discharge_value, + self.discharge_option) + self._update() + + def generate_height(self): + dlg = HeightDialog(self.height_values, + self.height_option, + trad=self._trad, + parent=self) + if dlg.exec(): + self.height_values = dlg.values + self.height_option = dlg.option + self._table.generate("height", + self.height_values, + self.height_option) self._update() diff --git a/src/View/InitialConditions/translate.py b/src/View/InitialConditions/translate.py index 3ed1c398..0edee4dc 100644 --- a/src/View/InitialConditions/translate.py +++ b/src/View/InitialConditions/translate.py @@ -28,11 +28,7 @@ class ICTranslate(MainTranslate): super(ICTranslate, self).__init__() self._dict["Initial condition"] = _translate( - "InitialCondition", "Initial condition") - self._dict["Discharge"] = _translate( - "InitialCondition", "Discharge") - self._dict["Height"] = _translate( - "InitialCondition", "Height") + "InitialCondition", "Initial conditions") self._dict["elevation"] = self._dict["unit_elevation"] self._dict["discharge"] = self._dict["unit_discharge"] diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py index fdcfcc70..13ce542c 100644 --- a/src/View/MainWindow.py +++ b/src/View/MainWindow.py @@ -499,6 +499,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): self.update() def close_model(self): + if not self.dialog_close(cancel=True): + return + self._study = None self.update_enable_action() self.conf.set_close_correctly() @@ -748,15 +751,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): ################## def msg_select_reach(self): - self.message_box("Please select a reach", - "Geometry edition need a reach selected " - "into river network window to work on it") + self.message_box( + self._trad["mb_select_reach_title"], + self._trad["mb_select_reach_msg"] + ) def dialog_reopen_study(self): dlg = QMessageBox(self) - dlg.setWindowTitle("Last open study") - dlg.setText("Do you want to open again the last open study?") + dlg.setWindowTitle(self._trad["mb_last_open_title"]) + dlg.setText(self._trad["mb_last_open_msg"]) opt = QMessageBox.Cancel | QMessageBox.Ok # | QMessageBox.Open dlg.setStandardButtons(opt) @@ -796,9 +800,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): def dialog_close(self, cancel=True): dlg = QMessageBox(self) - dlg.setWindowTitle("Close PAMHYR without saving study") - dlg.setText("Do you want to save current study before PAMHYR close ?") - opt = QMessageBox.Save | QMessageBox.Ignore + dlg.setWindowTitle(self._trad["mb_close_title"]) + dlg.setText(self._trad["mb_close_msg"]) + opt = QMessageBox.Save | QMessageBox.Close if cancel: opt |= QMessageBox.Cancel @@ -810,7 +814,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): if res == QMessageBox.Save: self.save_study() return True - elif res == QMessageBox.Ignore: + elif res == QMessageBox.Close: return True elif res == QMessageBox.Cancel: return False @@ -1446,15 +1450,3 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): f"sqlitebrowser {file}", shell=True ) - - # TODO: Delete me ! - ############### - # DUMMY STUFF # - ############### - - def open_dummy(self, title="Dummy"): - self.dummy = DummyWindow( - title=title if type(title) is str else "Dummy", - parent=self - ) - self.dummy.show() diff --git a/src/View/Network/GraphWidget.py b/src/View/Network/GraphWidget.py index 53ac9c15..1e7edc8c 100644 --- a/src/View/Network/GraphWidget.py +++ b/src/View/Network/GraphWidget.py @@ -368,11 +368,18 @@ class EdgeItem(QGraphicsItem): class NodeText(QGraphicsTextItem): - def __init__(self, node_item): + def __init__(self, node_item, graph): super(NodeText, self).__init__() self.item = node_item - self.setPlainText(self.item.node.name) + self.graph = graph + + name = self.item.node.name + if name == "": + name = self.graph._trad.node_name(self.item.node) + + self.setPlainText(name) + self.setDefaultTextColor(Qt.black) self.set_custom_pos(self.item.pos()) self.setZValue(2) @@ -502,7 +509,7 @@ class GraphWidget(QGraphicsView): for node in self.graph.nodes(): inode = NodeItem(node, self) - self.texts[inode] = NodeText(inode) + self.texts[inode] = NodeText(inode, self) self.scene().addItem(self.texts[inode]) self.scene().addItem(inode) self.node_items.append(inode) @@ -578,7 +585,7 @@ class GraphWidget(QGraphicsView): inode = NodeItem(node, self) self.scene().addItem(inode) self.node_items.append(inode) - self.texts[inode] = NodeText(inode) + self.texts[inode] = NodeText(inode, self) self.scene().addItem(self.texts[inode]) self.changeNode.emit(self.sender()) diff --git a/src/View/Network/Table.py b/src/View/Network/Table.py index 8ba889bd..97a26548 100644 --- a/src/View/Network/Table.py +++ b/src/View/Network/Table.py @@ -94,6 +94,9 @@ class NodeTableModel(PamhyrTableModel): return ret + if self._headers[index.column()] == "name": + return self._trad.node_name(self._lst[index.row()]) + return self._lst[index.row()][self._headers[index.column()]] @pyqtSlot() @@ -133,6 +136,15 @@ class EdgeTableModel(PamhyrTableModel): if role != Qt.ItemDataRole.DisplayRole: return QVariant() + if (self._headers[index.column()] == "node1" or + self._headers[index.column()] == "node2"): + return self._trad.node_name( + self._lst[index.row()][self._headers[index.column()]] + ) + + if self._headers[index.column()] == "name": + return self._trad.edge_name(self._lst[index.row()]) + return self._lst[index.row()][self._headers[index.column()]] @pyqtSlot() diff --git a/src/View/Network/Window.py b/src/View/Network/Window.py index 3f619b17..fc518b4e 100644 --- a/src/View/Network/Window.py +++ b/src/View/Network/Window.py @@ -97,6 +97,7 @@ class NetworkWindow(PamhyrWindow): table_view=table, table_headers=self._table_headers_node, editable_headers=node_editable_headers, + trad=self._trad, data=self._graph, undo=self._undo_stack, ) @@ -116,6 +117,7 @@ class NetworkWindow(PamhyrWindow): table_view=table, table_headers=self._table_headers_edge, editable_headers=edge_editable_headers, + trad=self._trad, delegates={ "node1": self.delegate_combobox, "node2": self.delegate_combobox, diff --git a/src/View/Network/translate.py b/src/View/Network/translate.py index ea999db2..da3e9618 100644 --- a/src/View/Network/translate.py +++ b/src/View/Network/translate.py @@ -31,6 +31,9 @@ class NetworkTranslate(MainTranslate): "Network", "River network" ) + self._dict["node"] = _translate("Network", "Node") + self._dict["edge"] = _translate("Network", "Reach") + self._dict["menu_add_node"] = _translate("Network", "Add node") self._dict["menu_del_node"] = _translate("Network", "Delete the node") self._dict["menu_edit_res_node"] = _translate( @@ -63,3 +66,15 @@ class NetworkTranslate(MainTranslate): "node2": _translate("Network", "Destination node"), "id": _translate("Network", "Index"), } + + def node_name(self, node): + if node.name == "": + return f"{self['node']} #{node.id}" + + return node.name + + def edge_name(self, edge): + if edge.name == "": + return f"{self['edge']} #{edge.id}" + + return edge.name diff --git a/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py b/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py index 796934fb..e88e5386 100644 --- a/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py +++ b/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py @@ -95,4 +95,5 @@ class CustomPlotValuesSelectionDialog(PamhyrDialog): ) self.value = x, y + super().accept() diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index a3073c7a..f1f2438f 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -34,7 +34,7 @@ unit = { "water_elevation": "0-meter", "discharge": "1-m3s", "velocity": "2-ms", - "depth": "3-meter", + "max_depth": "3-meter", "mean_depth": "3-meter", "froude": "4-dimensionless", "wet_area": "5-m2", @@ -157,9 +157,9 @@ class CustomPlot(PamhyrPlot): ) lines["velocity"] = line - if "depth" in self._y: + if "max_depth" in self._y: - ax = self._axes[unit["depth"]] + ax = self._axes[unit["max_depth"]] d = list( map( lambda p: p.geometry.max_water_depth( @@ -171,7 +171,7 @@ class CustomPlot(PamhyrPlot): rk, d, color='brown', lw=1., ) - lines["depth"] = line + lines["max_depth"] = line if "mean_depth" in self._y: @@ -361,9 +361,9 @@ class CustomPlot(PamhyrPlot): ) lines["velocity"] = line - if "depth" in self._y: + if "max_depth" in self._y: - ax = self._axes[unit["depth"]] + ax = self._axes[unit["max_depth"]] d = list( map(lambda z: profile.geometry.max_water_depth(z), z) ) @@ -372,7 +372,7 @@ class CustomPlot(PamhyrPlot): ts, d, color='brown', lw=1., ) - lines["depth"] = line + lines["max_depth"] = line if "mean_depth" in self._y: @@ -385,7 +385,7 @@ class CustomPlot(PamhyrPlot): ts, d, color='orange', lw=1., ) - lines["depth"] = line + lines["mean_depth"] = line if "froude" in self._y: diff --git a/src/View/Results/CustomPlot/Translate.py b/src/View/Results/CustomPlot/Translate.py index b00bab0a..abc6b244 100644 --- a/src/View/Results/CustomPlot/Translate.py +++ b/src/View/Results/CustomPlot/Translate.py @@ -59,20 +59,3 @@ class CustomPlotTranslate(ResultsTranslate): self._dict['3-meter'] = self._dict["unit_height"] self._dict['4-dimensionless'] = self._dict["unit_froude"] self._dict['5-m2'] = self._dict["wet_area"] - - # SubDict - - self._sub_dict["values_x"] = { - "rk": self._dict["rk"], - "time": self._dict["time"], - } - self._sub_dict["values_y"] = { - "elevation": self._dict["elevation"], - "water_elevation": self._dict["water_elevation"], - "discharge": self._dict["discharge"], - "velocity": self._dict["velocity"], - "depth": self._dict["max_depth"], - "mean_depth": self._dict["mean_depth"], - "froude": self._dict["froude"], - "wet_area": self._dict["wet_area"], - } diff --git a/src/View/Results/PlotH.py b/src/View/Results/PlotH.py index 90aa6674..5dc25831 100644 --- a/src/View/Results/PlotH.py +++ b/src/View/Results/PlotH.py @@ -51,8 +51,8 @@ class PlotH(PamhyrPlot): self._current_reach_id = reach_id self._current_profile_id = profile_id - self.label_x = _translate("Results", "Time (s)") - self.label_y = _translate("Results", "Discharge (m³/s)") + self.label_x = self._trad["unit_time_s"] + self.label_y = self._trad["unit_discharge"] self.label_discharge = _translate("Results", "Cross-section discharge") self.label_discharge_max = _translate("Results", "Max discharge") @@ -174,12 +174,12 @@ class PlotH(PamhyrPlot): self._line.set_data(x, y) self._current.set_data( - self._current_timestamp, + [self._current_timestamp, self._current_timestamp], self.canvas.axes.get_ylim() ) def update_current(self): self._current.set_data( - self._current_timestamp, + [self._current_timestamp, self._current_timestamp], self.canvas.axes.get_ylim() ) diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 03be2da7..eb4684cf 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -20,6 +20,8 @@ import os import csv import logging +from numpy import sqrt + from datetime import datetime from tools import trace, timer, logger_exception @@ -290,9 +292,9 @@ class ResultsWindow(PamhyrWindow): pname = profile.name if profile.name != "" else profile.rk - return (f"Reach: {reach.name} | " + - f"Profile: {pname} | " + - f"Timestamp : {fts} ({ts} sec)") + return (f"{self._trad['reach']}: {reach.name} | " + + f"{self._trad['cross_section']}: {pname} | " + + f"{self._trad['unit_time_s']} : {fts} ({ts} sec)") def setup_statusbar(self): txt = self._compute_status_label() @@ -308,8 +310,8 @@ class ResultsWindow(PamhyrWindow): actions = { "action_reload": self._reload, "action_add": self._add_custom_plot, - # "action_export": self.export, - "action_export": self.export_current, + "action_export": self._export, + # "action_export": self.export_current, } for action in actions: @@ -534,6 +536,7 @@ class ResultsWindow(PamhyrWindow): grid.addWidget(canvas, 1, 0) widget.setLayout(grid) tab_widget.addTab(widget, name) + tab_widget.setCurrentWidget(widget) def _copy(self): logger.info("TODO: copy") @@ -582,18 +585,50 @@ class ResultsWindow(PamhyrWindow): self._button_last.setEnabled(True) self._button_play.setIcon(self._icon_start) - def export(self): + def _export(self): + + dlg = CustomPlotValuesSelectionDialog(parent=self) + if dlg.exec(): + x, y = dlg.value + else: + return + + logger.debug( + "Export custom plot for: " + + f"{x} -> {','.join(y)}" + ) self.file_dialog( - select_file=False, - callback=lambda d: self.export_to(d[0]) + select_file="AnyFile", + callback=lambda f: self.export_to(f[0], x, y), + default_suffix=".csv", + file_filter=["CSV (*.csv)"], ) - def export_to(self, directory): + def export_to(self, filename, x, y): timestamps = sorted(self._results.get("timestamps")) - for reach in self._results.river.reachs: - self.export_reach(reach, directory, timestamps) + if x == "rk": + timestamp = self._get_current_timestamp() + val_dict = self._export_rk(timestamp, y, filename) + elif x == "time": + profile = self._get_current_profile() + val_dict = self._export_time(profile, y, filename) - def export_reach(self, reach, directory, timestamps): + with open(filename, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=',', + quotechar='|', quoting=csv.QUOTE_MINIMAL) + dict_x = self._trad.get_dict("values_x") + dict_y = self._trad.get_dict("values_y") + header = [dict_x[x]] + for text in y: + header.append(dict_y[text]) + writer.writerow(header) + for row in range(len(val_dict[x])): + line = [val_dict[x][row]] + for var in y: + line.append(val_dict[var][row]) + writer.writerow(line) + + def export_all(self, reach, directory, timestamps): name = reach.name name = name.replace(" ", "-") if len(timestamps) == 1: @@ -607,59 +642,144 @@ class ResultsWindow(PamhyrWindow): with open(file_name, 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) - if len(timestamps) > 1: - writer.writerow(["name", "rk", "data-file"]) - for profile in reach.profiles: - p_file_name = os.path.join( - directory, - f"cs_{profile.geometry.id}.csv" - ) - - writer.writerow([ - profile.name, - profile.rk, - p_file_name - ]) - - self.export_profile(reach, - profile, - p_file_name, - timestamps) - else: - ts = timestamps[0] - writer.writerow(self._table["raw_data"]._headers) - for row in range(self._table["raw_data"].rowCount()): - line = [] - for column in range(self._table["raw_data"].columnCount()): - index = self._table["raw_data"].index(row, column) - line.append(self._table["raw_data"].data(index)) - writer.writerow(line) - - def export_profile(self, reach, profile, file_name, timestamps): - with open(file_name, 'w', newline='') as csvfile: - writer = csv.writer(csvfile, delimiter=',', - quotechar='|', quoting=csv.QUOTE_MINIMAL) - - writer.writerow(["timestamp", "z", "q"]) - - for ts in timestamps: - writer.writerow([ - ts, - profile.get_ts_key(ts, "Z"), - profile.get_ts_key(ts, "Q"), - ]) + ts = timestamps[0] + writer.writerow(self._table["raw_data"]._headers) + for row in range(self._table["raw_data"].rowCount()): + line = [] + for column in range(self._table["raw_data"].columnCount()): + index = self._table["raw_data"].index(row, column) + line.append(self._table["raw_data"].data(index)) + writer.writerow(line) def export_current(self): self.file_dialog( - select_file=False, + select_file="Directory", callback=lambda d: self.export_current_to(d[0]) ) def export_current_to(self, directory): reach = self._results.river.reachs[self._get_current_reach()] - self.export_reach(reach, directory, [self._get_current_timestamp()]) + self.export_all(reach, directory, [self._get_current_timestamp()]) def delete_tab(self, index): tab_widget = self.find(QTabWidget, f"tabWidget") self._additional_plot.pop(tab_widget.tabText(index)) tab_widget.removeTab(index) + + def _export_rk(self, timestamp, y, filename): + reach = self._results.river.reachs[self._get_current_reach()] + my_dict = {} + my_dict["rk"] = reach.geometry.get_rk() + if "elevation" in y: + my_dict["elevation"] = reach.geometry.get_z_min() + if "discharge" in y: + my_dict["discharge"] = list( + map( + lambda p: p.get_ts_key(timestamp, "Q"), + reach.profiles + ) + ) + if "water_elevation" in y: + my_dict["water_elevation"] = list( + map( + lambda p: p.get_ts_key(timestamp, "Z"), + reach.profiles + ) + ) + if "velocity" in y: + my_dict["velocity"] = list( + map( + lambda p: p.geometry.speed( + p.get_ts_key(timestamp, "Q"), + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) + if "max_depth" in y: + my_dict["max_depth"] = list( + map( + lambda p: p.geometry.max_water_depth( + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) + if "mean_depth" in y: + my_dict["mean_depth"] = list( + map( + lambda p: p.geometry.mean_water_depth( + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) + if "froude" in y: + my_dict["froude"] = list( + map( + lambda p: + p.geometry.speed( + p.get_ts_key(timestamp, "Q"), + p.get_ts_key(timestamp, "Z")) / + sqrt(9.81 * ( + p.geometry.wet_area( + p.get_ts_key(timestamp, "Z")) / + p.geometry.wet_width( + p.get_ts_key(timestamp, "Z")) + )), + reach.profiles + ) + ) + if "wet_area" in y: + my_dict["wet_area"] = list( + map( + lambda p: p.geometry.wet_area( + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) + + return my_dict + + def _export_time(self, profile, y, filename): + reach = self._results.river.reachs[self._get_current_reach()] + profile = reach.profile(profile) + ts = list(self._results.get("timestamps")) + ts.sort() + my_dict = {} + my_dict["time"] = ts + z = profile.get_key("Z") + q = profile.get_key("Q") + if "elevation" in y: + my_dict["elevation"] = [profile.geometry.z_min()] * len(ts) + if "discharge" in y: + my_dict["discharge"] = q + if "water_elevation" in y: + my_dict["water_elevation"] = z + if "velocity" in y: + my_dict["velocity"] = list( + map( + lambda q, z: profile.geometry.speed(q, z), + q, z + ) + ) + if "max_depth" in y: + my_dict["max_depth"] = list( + map(lambda z: profile.geometry.max_water_depth(z), z) + ) + if "mean_depth" in y: + my_dict["mean_depth"] = list( + map(lambda z: profile.geometry.mean_water_depth(z), z) + ) + if "froude" in y: + my_dict["froude"] = list( + map(lambda z, q: + profile.geometry.speed(q, z) / + sqrt(9.81 * ( + profile.geometry.wet_area(z) / + profile.geometry.wet_width(z)) + ), z, q) + ) + if "wet_area" in y: + my_dict["wet_area"] = list( + map(lambda z: profile.geometry.wet_area(z), z) + ) + + return my_dict diff --git a/src/View/Results/translate.py b/src/View/Results/translate.py index faab6ecb..8b66124e 100644 --- a/src/View/Results/translate.py +++ b/src/View/Results/translate.py @@ -37,7 +37,7 @@ class ResultsTranslate(MainTranslate): self._dict['x'] = _translate("Results", "X (m)") - self._dict['label_bottom'] = _translate("Results", "River bottom") + self._dict['label_bottom'] = _translate("Results", "Bottom") self._dict['label_water'] = _translate("Results", "Water elevation") self._dict['label_water_max'] = _translate( "Results", @@ -66,3 +66,19 @@ class ResultsTranslate(MainTranslate): "hydraulic_radius": self._dict["unit_hydraulic_radius"], "froude": self._dict["unit_froude"], } + + self._sub_dict["values_x"] = { + "rk": self._dict["unit_rk"], + "time": self._dict["unit_time_s"], + } + + self._sub_dict["values_y"] = { + "elevation": self._dict["unit_elevation"], + "water_elevation": self._dict["unit_water_elevation"], + "discharge": self._dict["unit_discharge"], + "velocity": self._dict["unit_speed"], + "max_depth": self._dict["unit_max_height"], + "mean_depth": self._dict["unit_mean_height"], + "froude": self._dict["unit_froude"], + "wet_area": self._dict["unit_wet_area"], + } diff --git a/src/View/Tools/ASubWindow.py b/src/View/Tools/ASubWindow.py index 1825a7b4..2ea5d1f9 100644 --- a/src/View/Tools/ASubWindow.py +++ b/src/View/Tools/ASubWindow.py @@ -85,7 +85,7 @@ class WindowToolKit(object): return header, values - def file_dialog(self, select_file=True, + def file_dialog(self, select_file="ExistingFile", callback=lambda x: None, directory=None, default_suffix=None, @@ -107,16 +107,18 @@ class WindowToolKit(object): dialog = QFileDialog(self, options=options) - if select_file: + if select_file == "Existing_file": mode = QFileDialog.FileMode.ExistingFile - else: + elif select_file == "Directory": mode = QFileDialog.FileMode.Directory + else: + mode = QFileDialog.FileMode.AnyFile dialog.setFileMode(mode) if directory is not None: dialog.setDirectory(directory) - if select_file: + if select_file != "Directory": if default_suffix is not None: dialog.setDefaultSuffix(default_suffix) @@ -147,6 +149,9 @@ class WindowToolKit(object): msg.setInformativeText(informative_text) msg.setWindowTitle(window_title) + msg.findChild(QLabel, "qt_msgbox_label")\ + .setFixedWidth(384) + msg.exec_() diff --git a/src/View/Translate.py b/src/View/Translate.py index f819660a..0a83bf3b 100644 --- a/src/View/Translate.py +++ b/src/View/Translate.py @@ -66,8 +66,9 @@ class UnitTranslate(CommonWordTranslate): self._dict["unit_water_elevation"] = _translate( "Unit", "Water elevation (m)" ) + self._dict["unit_speed"] = _translate("Unit", "Velocity (m/s)") - self._dict["unit_discharge"] = _translate("Unit", "Discharge (m³/s)") + self._dict["unit_discharge"] = _translate("Unit", "Discharge (m^3/s)") self._dict["unit_area"] = _translate("Unit", "Area (hectare)") self._dict["unit_time_s"] = _translate("Unit", "Time (sec)") @@ -76,7 +77,7 @@ class UnitTranslate(CommonWordTranslate): self._dict["unit_date_s"] = _translate("Unit", "Date (sec)") self._dict["unit_date_iso"] = _translate("Unit", "Date (ISO format)") - self._dict["unit_wet_area"] = _translate("Unit", "Wet Area (m²)") + self._dict["unit_wet_area"] = _translate("Unit", "Wet Area (m^2)") self._dict["unit_wet_perimeter"] = _translate( "Unit", "Wet Perimeter (m)" ) @@ -113,3 +114,29 @@ class MainTranslate(UnitTranslate): self._dict["waiting_result"] = _translate( "MainWindow", "Reading results ..." ) + + # Message box + self._dict["mb_select_reach_title"] = _translate( + "MainWindow", "Please select a reach" + ) + self._dict["mb_select_reach_msg"] = _translate( + "MainWindow", + "This edition window need a reach selected " + "into the river network to work on it" + ) + + self._dict["mb_last_open_title"] = _translate( + "MainWindow", "Last open study" + ) + self._dict["mb_last_open_msg"] = _translate( + "MainWindow", + "Do you want to open again the last open study?" + ) + + self._dict["mb_close_title"] = _translate( + "MainWindow", "Close without saving study" + ) + self._dict["mb_close_msg"] = _translate( + "MainWindow", + "Do you want to save current study before closing it?" + ) diff --git a/src/View/ui/ConfigureDialog.ui b/src/View/ui/ConfigureDialog.ui index 9c23ce2d..adcab8b5 100644 --- a/src/View/ui/ConfigureDialog.ui +++ b/src/View/ui/ConfigureDialog.ui @@ -382,7 +382,7 @@ - Langue + Language diff --git a/src/View/ui/CustomPlotValuesSelectionDialog.ui b/src/View/ui/CustomPlotValuesSelectionDialog.ui index 88ca363c..f7daec37 100644 --- a/src/View/ui/CustomPlotValuesSelectionDialog.ui +++ b/src/View/ui/CustomPlotValuesSelectionDialog.ui @@ -7,7 +7,7 @@ 0 0 414 - 482 + 70 @@ -57,22 +57,6 @@ - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - buttonBox rejected() @@ -89,5 +73,21 @@ + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + diff --git a/src/View/ui/EditBoundaryConditions.ui b/src/View/ui/EditBoundaryConditions.ui index c2c81378..f24120f4 100644 --- a/src/View/ui/EditBoundaryConditions.ui +++ b/src/View/ui/EditBoundaryConditions.ui @@ -75,6 +75,7 @@ + @@ -140,6 +141,14 @@ Generate rating curve as Q(z) = Sqrt(g*S(z)^3/L(z)) + + + Make increasing + + + Remove points to make the curve increasing + + diff --git a/src/View/ui/InitialConditions.ui b/src/View/ui/InitialConditions.ui index 68ed2f94..81faeb6a 100644 --- a/src/View/ui/InitialConditions.ui +++ b/src/View/ui/InitialConditions.ui @@ -6,7 +6,7 @@ 0 0 - 1024 + 849 576 @@ -27,14 +27,21 @@ - Generate height + Generate uniform depth - Generate discharge + Generate uniform discharge + + + + + + + Generate uniform elevation @@ -65,7 +72,7 @@ 0 0 - 1024 + 849 22 diff --git a/src/View/ui/InitialConditions_Dialog_Generator_Depth.ui b/src/View/ui/InitialConditions_Dialog_Generator_Depth.ui new file mode 100644 index 00000000..deb89169 --- /dev/null +++ b/src/View/ui/InitialConditions_Dialog_Generator_Depth.ui @@ -0,0 +1,95 @@ + + + Dialog + + + + 0 + 0 + 284 + 107 + + + + Dialog + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Depth (m) + + + + + + + 1000000.000000000000000 + + + + + + + + + Generate discharge + + + true + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui b/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui index 58ae2b98..0390f175 100644 --- a/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui +++ b/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui @@ -32,7 +32,7 @@ - Discharge (m³/s) + Discharge (m^3/s) diff --git a/src/View/ui/InitialConditions_Dialog_Generator_Height.ui b/src/View/ui/InitialConditions_Dialog_Generator_Height.ui index 500a2b0b..36d77a44 100644 --- a/src/View/ui/InitialConditions_Dialog_Generator_Height.ui +++ b/src/View/ui/InitialConditions_Dialog_Generator_Height.ui @@ -6,8 +6,8 @@ 0 0 - 284 - 80 + 396 + 182 @@ -17,7 +17,79 @@ - + + + + + + Upstream height (m) + + + + + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + + + + + false + + + Discharge + + + + + + + false + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + + + + + Downstream height (m) + + + + + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + 0.000000000000000 + + + + + + Qt::Horizontal @@ -27,34 +99,17 @@ - - + + - + - Height (m) - - - - - - - 1000000.000000000000000 + Generate constant discharge - - - - Generate discharge - - - true - - - diff --git a/src/View/ui/PurgeOptions.ui b/src/View/ui/PurgeOptions.ui index 9b027f97..2571a6be 100644 --- a/src/View/ui/PurgeOptions.ui +++ b/src/View/ui/PurgeOptions.ui @@ -6,7 +6,7 @@ 0 0 - 194 + 251 114 @@ -27,7 +27,7 @@ - Number of points to keep + Maximum number of points to keep diff --git a/src/View/ui/Results.ui b/src/View/ui/Results.ui index 256ab368..90ca6fb4 100644 --- a/src/View/ui/Results.ui +++ b/src/View/ui/Results.ui @@ -132,14 +132,14 @@ - 0 + 1 true - Raw data + Data @@ -262,7 +262,7 @@ Export - Export raw data + Export data as CSV Ctrl+E diff --git a/src/View/ui/about.ui b/src/View/ui/about.ui index 36f0efd7..1b1041bb 100644 --- a/src/View/ui/about.ui +++ b/src/View/ui/about.ui @@ -17,8 +17,35 @@ - + + + + + + + + + ressources/Pamhyr2_logo.png + + + + + + + + + + ressources/Logo-INRAE.png + + + + + + + + 0 + @@ -33,34 +60,213 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copyright © 2022-2024 INRAE + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Version: @version + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + License: GPLv3+ + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 100 + 50 + + + + + + + ressources/GPLv3_Logo.png + + + true + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a> + + + Qt::RichText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + ... - - - - - - Version: @version - - - - - - - License: GPLv3+ - - - - - - - <a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a> - - - Qt::RichText + + Qt::AlignCenter @@ -79,16 +285,6 @@ - - - - - - - ressources/Pamhyr2_logo.png - - - diff --git a/src/lang/fr.ts b/src/lang/fr.ts index dc68faec..4d6f0091 100644 --- a/src/lang/fr.ts +++ b/src/lang/fr.ts @@ -180,8 +180,8 @@ - Discharge weir - + Rectangular weir + Déversoir rectangulaire @@ -192,17 +192,17 @@ Modifier les conditions aux limites - + X X - + Y Y - + Solid (kg/s) Solide (kg/s) @@ -236,6 +236,11 @@ Node Nœud + + + Boundary Condition Options + Options des conditions limites + BoundaryConditions @@ -270,21 +275,11 @@ Mage geometry guideline checker Vérificateur des lignes directrices pour Mage - - - Check if geometry guidelines are correctly defined for each reach - Vérifie si les lignes directrices sont définies pour tous les biefs - Study reach network checker Vérificateur des biefs de l'étude - - - Check if at least one reach exists - Vérifie s'il existe au moins un bief dans l'étude - Study geometry checker @@ -448,7 +443,7 @@ Sélection des graphiques personnalisés - + Bed elevation (m) Cote du fond (m) @@ -533,11 +528,6 @@ . . - - - Height - Hauteur - Solvers @@ -568,11 +558,6 @@ HH:mm:ss HH:mm:ss - - - Strickler coefficients - Coefficients de Strickler - Editor @@ -649,9 +634,9 @@ Axe Y : - + Discharge - Débit + Débit @@ -668,16 +653,6 @@ First guideline Première ligne directrice - - - Last guideline - Dernière ligne directrice - - - - Space step - Pas d'espace - Guideline used for distance computation @@ -718,20 +693,15 @@ Line Ligne - - - Comment lines start with '*' char (let see Mage documentation for more details) - Les lignes de commentaire commencent par un caractère '*' (voir la documentation de Mage pour plus de détails) - Limits Limites - + Options - Options + Options @@ -743,46 +713,36 @@ Type of interpolation: Type d'interpolation : - - - Height (m) - Hauteur (m) - Stricklers Stricklers - + Distance computation Calcule des distances - + Second guide-line Deuxième ligne directrice - + First guide-line Première ligne directrice - + Origin Origine - + Origin value Valeur de l'origine - - - Discharge (m³/s) - Débit (m³/s) - Parameters @@ -791,7 +751,7 @@ Langue - Langue + Langue @@ -803,10 +763,90 @@ Second guideline Seconde ligne directrice - - - Discharge (m³/s) - + + + Y coordinate + Coordonnées Y + + + + X coordinate + Coordonnées X + + + + Z coordinate + Coordonnées Z + + + + Upstream to downstream + De l'amont à l'aval + + + + Downstream to upstream + De l'aval à l'amont + + + + Orientation + Orientation + + + + Keep current + Actuelle + + + + Upstream height (m) + Cote à l'amont (m) + + + + Downstream height (m) + Cote à l'aval (m) + + + + Generate constant discharge + Générer un débit constant + + + + Slope + Pente + + + + Estimate + Estimer + + + + Generate height + Générer une profondeur + + + + Depth (m) + Profondeur (m) + + + + Generate discharge + Générer un débit + + + + Maximum number of points to keep + Nombre de points maxi à conserver + + + + Discharge (m^3/s) + Débit (m^3/s) @@ -929,27 +969,27 @@ Formulaire - + About Pamhyr2 À propos de Pamhyr2 - + ... ... - + Version: @version Version : @version - + License: GPLv3+ Licence : GPLv3+ - + <a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a> <a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Code source</a> @@ -983,16 +1023,6 @@ Study Étude - - - Description: - Description : - - - - Name: - Nom : - @study_name @@ -1098,11 +1128,6 @@ @nb_hs @nb_hs - - - Restart - Relancer - Checkers list @@ -1113,49 +1138,54 @@ Errors summary Résumé des erreurs + + + Copyright © 2022-2024 INRAE + Copyright © 2022-2024 INRAE + + + + Copyright © 2022-2024 INRAE + Copyright © 2022-2024 INRAE + Frictions - - Strickler coefficients - Coefficients de Strickler - - - - Edit friction coefficients - Modifier les frottements - - - + Start (m) PK de départ (m) - + End (m) PK de fin (m) - + Start coefficient Coefficient de départ - + End coefficient Coefficient de fin - + Stricklers Stricklers - + Edit frictions Éditer les frotements + + + Strickler ($m^{1/3}/s$) + Strickler ($m^{1/3}/s$) + Geometry @@ -1234,21 +1264,6 @@ Points Points - - - First guideline - Première ligne directrice - - - - Second guideline - Seconde ligne directrice - - - - Mean over the two guidelines - Moyenne sur les deux lignes directrices - Meshing @@ -1309,6 +1324,21 @@ the mean over the two guide-lines la moyenne entre les deux ligne directrice + + + UpdateRK + Mise à jour PK + + + + Purge + Simplifier + + + + Shift + Translater + HydraulicStructures @@ -1327,18 +1357,8 @@ InitialCondition - Initial condition - Condition initiale - - - - Discharge - Débit - - - - Height - Hauteur + Initial conditions + Conditions initiales @@ -1387,17 +1407,17 @@ MainWindow - + Open debug window Ouvrir la fenêtre de débogage - + Open SQLite debuging tool ('sqlitebrowser') Ouvrir l'outil de débogage SQLite ('sqlitebrowser') - + Enable this window Activer cette fenêtre @@ -1407,12 +1427,7 @@ Fenêtre principale - - Toolbar - Barre d'outils - - - + Add Ajouter @@ -1432,7 +1447,7 @@ Supprimer les casier(s) - + Edit modifier @@ -1622,12 +1637,12 @@ Modifier la géométrie - + Import geometry Importer une géométrie - + Export geometry Exporter la géométrie @@ -1746,21 +1761,11 @@ Friction Frottements - - - Edit friction coefficients - Modifier les frottements - Edit study Modifier l'étude - - - Edit the study metadata - Modifier les informations de l'étude - Define initial conditions @@ -1952,42 +1957,37 @@ Modifier une condition aux limites ou un apport ponctuel - + Raw data - Données brutes + Données brutes - + Water elevation Cote de l'eau - + Discharge time series Hydrogramme - + Add customized visualization Ajouter une visualisation personnalisée - + Reload Recharger - + Export Exporter - - Export raw data - Exporter les données brutes - - - + delete supprimer @@ -2042,7 +2042,7 @@ Activer/Désactiver l'ouvrage hydraulique élémentaire - + Add a new point Ajouter un nouveau point @@ -2092,7 +2092,7 @@ Supprimer les points sélectionnés - + Up Monter @@ -2102,7 +2102,7 @@ Déplacer les points sélectionnés vers le haut - + Down Descendre @@ -2112,7 +2112,7 @@ Déplacer les points sélectionnés vers le bas - + sort_asc sort_asc @@ -2122,7 +2122,7 @@ Trier les points par leurs plus proches voisins - + sort_des sort_des @@ -2167,45 +2167,25 @@ Modifier les couches sédimentaires du profil - - Generate height - Générer une hauteur - - - - Generate discharge - Générer un débit - - - + Add new initial condition Ajouter une nouvelle condition initiale - + Delete inital condition Supprimer une condition initiale - + sort sort - + Sort inital conditions Trier les conditions initiales - - - Add a new point in boundary condition or point source - Ajouter un nouveau point - - - - Sort boundary condition points - Trier les points des conditions aux limites - Add a new point in boundary condition or lateral source @@ -2237,57 +2217,57 @@ Modifier les couches sédimentaires - + Import Importer - + Add a cross-section Ajouter une section en travers - + Delete selected cross-section(s) Supprimer les sections en travers sélectionnées - + Edit selected cross section(s) Modifier les sections en travers sélectionnées - + Sort cross-sections by ascending position Trier les sections en travers par PK croissant - + Sort cross-sections by descending position Trier les sections en travers par PK décroissant - + Move up selected cross-section(s) Déplacer les sections en travers vers le haut - + Move down selected cross-section(s) Déplacer les sections en travers vers le bas - + Meshing Maillage - + Summary Résumé - + Checks Vérifications @@ -2382,14 +2362,14 @@ Modifier les informations de l'étude - + toolBar - + toolBar toolBar_2 - + toolBar_2 @@ -2397,7 +2377,7 @@ Éditer les frotements - + Purge Purger @@ -2417,22 +2397,22 @@ Retourner l'ordre des points - + Import from file Importer depuis un fichier - + Update RK Mise à jour des PK - + Recompute RK Recalcule des PK - + Purge cross-sections to keep a given number of points Purger les profiles pour garder un nombre fixer de points @@ -2441,6 +2421,106 @@ Sort points Trier les points + + + Export data as CSV + Exporter les données au format CSV + + + + Generate uniform + Générer un regime uniforme + + + + Generate rating curve from Manning law + Générer une courbe de tarage (loi de Maning) + + + + Generate critical + Générer régime critique + + + + Generate rating curve as Q(z) = Sqrt(g*S(z)^3/L(z)) + Générer une courbe de tarage (Q(z) = Sqrt(g*S(z)^3/L(z))) + + + + Make increasing + Augmenter + + + + Remove points to make the curve increasing + Supprimer des points pour rendre la courbe croissante + + + + Shift + Translater + + + + Shift selected sections coordinates + Translater les coordonnées des sections sélectionnées + + + + Generate uniform depth + Générer une profondeur uniforme + + + + Generate uniform discharge + Générer un débit uniforme + + + + Generate uniform elevation + Générer une cote uniforme + + + + Data + Données + + + + Please select a reach + Veuillez sélectionner un bief + + + + Geometry edition need a reach selected into the river network to work on it + L'édition de la géométrie nécessite un bief sélectionné dans le réseau fluvial pour pouvoir travailler dessus + + + + Last open study + Dernière étude ouverte + + + + Do you want to open again the last open study? + Voulez-vous rouvrir la dernière étude ? + + + + This edition window need a reach selected into the river network to work on it + Cette fenêtre d'édition a besoin d'un bief sélectionné dans le réseau pour travailler dessus + + + + Close without saving study + Fermer sans sauvegarder l'étude + + + + Do you want to save current study before closing it? + Souhaitez-vous sauvegarder l'étude en cours avant de la fermer ? + MainWindow_reach @@ -2452,7 +2532,7 @@ Height (m) - Hauteur (m) + Cote (m) @@ -2462,7 +2542,7 @@ Select destination file - Selectionner fichier de destination + Sélectionner fichier de destination @@ -2473,60 +2553,75 @@ Réseau - + Add node Ajouter un nœud - + Delete the node Supprimer le nœud - + Edit node reservoir Modifier le casier du nœud - + Add node reservoir Ajouter un casier au nœud - + Delete the reach Supprimer l'arrête - + Enable the reach Activer l'arête - + Disable the reach Désactiver l'arête - + Reverse the reach orientation Inverser l'orientation de l'arête - + Source node Nœud source - + Destination node Nœud destination - + Delete node reservoir Supprimer le casier du nœud + + + Node + Nœud + + + + Reach + Bief + + + + Index + Index + Pamhyr @@ -2585,15 +2680,10 @@ X (m) - + Y (m) Y (m) - - - Time (s) - Temps (s) - Cross-section discharge @@ -2622,7 +2712,7 @@ River bottom - Fond de la rivière + Fond de la rivière @@ -2645,14 +2735,9 @@ Profil - - Discharge (m³/s) - Débit (m³/s) - - - - Discharge (m³/s) - + + Bottom + Fond @@ -2733,7 +2818,7 @@ Select solver - Selection du solveur + Sélection du solveur @@ -2846,7 +2931,7 @@ Minimum water height (meter) - Hauteur d'eau minimale (mètres) + Profondeur minimale (mètres) @@ -2981,11 +3066,6 @@ Strickler coefficients Coefficient de Strickler - - - Stricklers - Stricklers - Study @@ -3045,11 +3125,6 @@ Unit - - - Position (m) - PK (m) - Width (m) @@ -3058,67 +3133,97 @@ Depth (m) - Hauteur (m) + Profondeur (m) - + Diameter (m) Diamètre (m) - + Thickness (m) Épaisseur (m) - + Elevation (m) Cote (m) - + Water elevation (m) Cote de l'eau (m) - + Area (hectare) Aire (hectare) - + Time (sec) Temps (s) - + Time (JJJ:HH:MM:SS) Temps (JJJ:HH:MM:SS) - + Date (sec) Date (s) - + Date (ISO format) Date (format ISO) - - Discharge (m³/s) - Débit (m³/s) + + River Kilometer (m) + Point Kilométrique (m) - - Speed (m/s) + + Max Depth (m) + Profondeur max (m) + + + + Mean Depth (m) + Profondeur moyenne (m) + + + + Velocity (m/s) Vitesse (m/s) - - - Discharge (m³/s) - + + + Wet Perimeter (m) + Périmètre mouiller (m) + + + + Hydraulic Radius (m) + Rayon hydraulique (m) + + + + Froude number + Nombre de Froude + + + + Discharge (m^3/s) + Débit (m^3/s) + + + + Wet Area (m^2) + Aire mouiller (m^2)