From 52c311887a2c147bd7b005d2c4f89fc1e2affe48 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 17 Jul 2024 11:36:57 +0200 Subject: [PATCH 01/29] more hydraulic values in raw results --- src/Model/Geometry/ProfileXYZ.py | 26 +++++++++++++++++++++++--- src/View/Results/Table.py | 29 ++++++++++++++++++++++++++++- src/View/Results/translate.py | 7 ++++++- src/View/Translate.py | 10 +++++++++- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 9d31a97d..ee765c22 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -402,12 +402,12 @@ class ProfileXYZ(Profile, SQLSubModel): return abs(rg.dist(rd)) def wet_perimeter(self, z): - poly = self.wet_polygon(z) + line = self.wet_line(z) - if poly is None: + if line is None: return 0 - return poly.length + return line.length def wet_area(self, z): poly = self.wet_polygon(z) @@ -417,6 +417,26 @@ class ProfileXYZ(Profile, SQLSubModel): return poly.area + def wet_radius(self, z): + p = self.wet_perimeter(z) + a = self.wet_area(z) + + if p == 0: + return 0 + + return a/p + + def wet_line(self, z): + points = self.wet_points(z) + if len(points) < 3: + return None + + z = map(lambda p: p.z, points) + station = self._get_station(points) + + line = geometry.LineString(list(zip(station, z))) + return line + def wet_polygon(self, z): points = self.wet_points(z) if len(points) < 3: diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py index 7ce91f38..1b16a882 100644 --- a/src/View/Results/Table.py +++ b/src/View/Results/Table.py @@ -86,12 +86,39 @@ class TableModel(PamhyrTableModel): elif self._headers[column] == "discharge": v = self._lst[row].get_ts_key(self._timestamp, "Q") return f"{v:.4f}" - elif self._headers[column] == "speed": + elif self._headers[column] == "velocity": q = self._lst[row].get_ts_key(self._timestamp, "Q") z = self._lst[row].get_ts_key(self._timestamp, "Z") v = self._lst[row].geometry.speed(q, z) return f"{v:.4f}" + elif self._headers[column] == "width": + pt_left, pt_right = self._lst[row].get_ts_key( + self._timestamp, + "water_limits" + ) + v = pt_left.dist_2d(pt_right) + return f"{v:.4f}" + elif self._headers[column] == "depth": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + z_min = self._lst[row].geometry.z_min() + v = z - z_min + return f"{v:.4f}" + elif self._headers[column] == "wet_area": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_area(z) + return f"{v:.4f}" + elif self._headers[column] == "wet_perimeter": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_perimeter(z) + return f"{v:.4f}" + elif self._headers[column] == "hydraulic_radius": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_radius(z) + return f"{v:.4f}" + else: + v = 0.0 + return f"{v:.4f}" return QVariant() diff --git a/src/View/Results/translate.py b/src/View/Results/translate.py index 55c9c45e..05ae7d6b 100644 --- a/src/View/Results/translate.py +++ b/src/View/Results/translate.py @@ -57,5 +57,10 @@ class ResultsTranslate(MainTranslate): "name": _translate("Results", "Profile"), "water_elevation": self._dict["unit_water_elevation"], "discharge": self._dict["unit_discharge"], - "speed": self._dict["unit_speed"], + "velocity": self._dict["unit_speed"], + "width": self._dict["unit_width"], + "depth": self._dict["unit_height"], + "wet_area": self._dict["unit_wet_area"], + "wet_perimeter": self._dict["unit_wet_perimeter"], + "hydraulic_radius": self._dict["unit_hydraulic_radius"], } diff --git a/src/View/Translate.py b/src/View/Translate.py index 16b198b5..a9c3d8ee 100644 --- a/src/View/Translate.py +++ b/src/View/Translate.py @@ -61,7 +61,7 @@ class UnitTranslate(CommonWordTranslate): self._dict["unit_water_elevation"] = _translate( "Unit", "Water elevation (m)" ) - self._dict["unit_speed"] = _translate("Unit", "Speed (m/s)") + self._dict["unit_speed"] = _translate("Unit", "Velocity (m/s)") self._dict["unit_discharge"] = _translate("Unit", "Discharge (m³/s)") self._dict["unit_area"] = _translate("Unit", "Area (hectare)") @@ -71,6 +71,14 @@ 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_perimeter"] = _translate( + "Unit", "Wet Perimeter (m)" + ) + self._dict["unit_hydraulic_radius"] = _translate( + "Unit", "Hydraulic Radius (m)" + ) + class MainTranslate(UnitTranslate): def __init__(self): From f6a2240b1fa943031a5aef862e4e38af01769cb9 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 17 Jul 2024 15:41:26 +0200 Subject: [PATCH 02/29] wet_area and wet_perimeter --- src/Model/Geometry/Profile.py | 6 +++ src/Model/Geometry/ProfileXYZ.py | 76 ++++++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py index 3f55eb49..b24e2eb9 100644 --- a/src/Model/Geometry/Profile.py +++ b/src/Model/Geometry/Profile.py @@ -356,3 +356,9 @@ class Profile(object): def wet_area(self, z): raise NotImplementedMethodeError(self, self.wet_area) + + def wet_radius(self, z): + raise NotImplementedMethodeError(self, self.wet_radius) + + def get_nb_wet_areas(self, z): + raise NotImplementedMethodeError(self, self.get_nb_wet_areas) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index ee765c22..bcbd42e9 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -402,19 +402,28 @@ class ProfileXYZ(Profile, SQLSubModel): return abs(rg.dist(rd)) def wet_perimeter(self, z): - line = self.wet_line(z) + lines = self.wet_lines(z) - if line is None: + if lines is None: return 0 - return line.length + length = 0.0 + for line in lines: + length += line.length + return length def wet_area(self, z): - poly = self.wet_polygon(z) + lines = self.wet_lines(z) - if poly is None: + if lines is None: return 0 + area = 0.0 + for line in lines: + poly = geometry.Polygon(line) + area += poly.area + return area + return poly.area def wet_radius(self, z): @@ -431,21 +440,57 @@ class ProfileXYZ(Profile, SQLSubModel): if len(points) < 3: return None - z = map(lambda p: p.z, points) + zz = map(lambda p: p.z, points) station = self._get_station(points) - line = geometry.LineString(list(zip(station, z))) + line = geometry.LineString(list(zip(station, zz))) return line + def wet_lines(self, z): + points = self._points + if len(points) < 3: + return None + + lines = [] + + zz = list(map(lambda p: p.z, points)) + station = self._get_station(points) + + line = [] + for i in range(self.number_points-1): + + if zz[i] >= z and zz[i+1] < z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + line.append([y,z]) + + if zz[i] < z: + line.append([station[i],zz[i]]) + + if zz[i] <= z and zz[i+1] > z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + line.append([y,z]) + lines.append(geometry.LineString(line)) + line = [] + + return lines + def wet_polygon(self, z): points = self.wet_points(z) if len(points) < 3: return None - z = map(lambda p: p.z, points) + zz = map(lambda p: p.z, points) station = self._get_station(points) - poly = geometry.Polygon(list(zip(station, z))) + poly = geometry.Polygon(list(zip(station, zz))) return poly def wet_points(self, z): @@ -457,6 +502,19 @@ class ProfileXYZ(Profile, SQLSubModel): return points + def get_nb_wet_areas(self, z): + + n_zones = 0 + points = self._points + if points[0].z <= z: + n_zones += 1 + + for i in range(self.number_points-1): + if points[i].z > z and points[i+1].z <= z: + n_zones += 1 + + return n_zones + def get_water_limits(self, z): """ Determine left and right limits of water elevation. From 04e5b617327a12f5515e84371f9d12a17416656f Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 17 Jul 2024 16:25:03 +0200 Subject: [PATCH 03/29] wet_width --- src/Model/Geometry/Profile.py | 3 ++ src/Model/Geometry/ProfileXYZ.py | 56 ++++++++++++++++++++++++++++++++ src/View/Results/Table.py | 7 ++-- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py index b24e2eb9..6a4b45ef 100644 --- a/src/Model/Geometry/Profile.py +++ b/src/Model/Geometry/Profile.py @@ -351,6 +351,9 @@ class Profile(object): def wet_points(self, z): raise NotImplementedMethodeError(self, self.wet_point) + def wet_width(self, z): + raise NotImplementedMethodeError(self, self.wet_width) + def wet_perimeter(self, z): raise NotImplementedMethodeError(self, self.wet_perimeter) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index bcbd42e9..d06728d7 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -401,6 +401,18 @@ class ProfileXYZ(Profile, SQLSubModel): return abs(rg.dist(rd)) + def wet_width(self, z): + start, end = self.get_all_water_limits_ac(z) + + if len(start) == 0: + return 0 + + length = 0.0 + for s,e in zip(start, end): + length += abs(s - e) + return length + + def wet_perimeter(self, z): lines = self.wet_lines(z) @@ -515,6 +527,50 @@ class ProfileXYZ(Profile, SQLSubModel): return n_zones + def get_all_water_limits_ac(self, z): + """ + Determine all water limits for z elevation. + """ + + points = self._points + if len(points) < 3: + return None + + zz = list(map(lambda p: p.z, points)) + station = self._get_station(points) + + start = [] + if points[0].z <= z: + start.append(station[0]) + + for i in range(self.number_points-1): + if zz[i] > z and zz[i+1] <= z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + start.append(y) + + end = [] + for i in reversed(range(self.number_points-1)): + if zz[i-1] <= z and zz[i] > z: + y = np.interp( + z, + [zz[i-1], zz[i]], + [station[i-1], station[i]] + ) + end.append(y) + + if points[-1].z <= z: + end.append(station[-1]) + + if len(start) != len(end): + logger.error(f"ERROR in get_all_water_limits_ac") + return [], [] + + return start, list(reversed(end)) + def get_water_limits(self, z): """ Determine left and right limits of water elevation. diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py index 1b16a882..7b49910c 100644 --- a/src/View/Results/Table.py +++ b/src/View/Results/Table.py @@ -93,11 +93,8 @@ class TableModel(PamhyrTableModel): v = self._lst[row].geometry.speed(q, z) return f"{v:.4f}" elif self._headers[column] == "width": - pt_left, pt_right = self._lst[row].get_ts_key( - self._timestamp, - "water_limits" - ) - v = pt_left.dist_2d(pt_right) + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_width(z) return f"{v:.4f}" elif self._headers[column] == "depth": z = self._lst[row].get_ts_key(self._timestamp, "Z") From 61fce32c609d0354b8001506d0eb3a9427361c81 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Tue, 30 Jul 2024 14:09:29 +0200 Subject: [PATCH 04/29] mean water depth --- src/Model/Geometry/ProfileXYZ.py | 12 ++++++++++++ src/View/Results/Table.py | 9 ++++++--- src/View/Results/translate.py | 3 ++- src/View/Translate.py | 4 +++- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index d06728d7..c06a2653 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -494,6 +494,18 @@ class ProfileXYZ(Profile, SQLSubModel): return lines + def max_water_depth(self, z): + return z - self.z_min() + + def mean_water_depth(self, z): + a = self.wet_area(z) + w = self.wet_width(z) + + if w == 0: + return 0 + + return a/w + def wet_polygon(self, z): points = self.wet_points(z) if len(points) < 3: diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py index 7b49910c..1088ac26 100644 --- a/src/View/Results/Table.py +++ b/src/View/Results/Table.py @@ -96,10 +96,13 @@ class TableModel(PamhyrTableModel): z = self._lst[row].get_ts_key(self._timestamp, "Z") v = self._lst[row].geometry.wet_width(z) return f"{v:.4f}" - elif self._headers[column] == "depth": + elif self._headers[column] == "max_depth": z = self._lst[row].get_ts_key(self._timestamp, "Z") - z_min = self._lst[row].geometry.z_min() - v = z - z_min + v = self._lst[row].geometry.max_water_depth(z) + return f"{v:.4f}" + elif self._headers[column] == "mean_depth": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.mean_water_depth(z) return f"{v:.4f}" elif self._headers[column] == "wet_area": z = self._lst[row].get_ts_key(self._timestamp, "Z") diff --git a/src/View/Results/translate.py b/src/View/Results/translate.py index 05ae7d6b..bf96ba13 100644 --- a/src/View/Results/translate.py +++ b/src/View/Results/translate.py @@ -59,7 +59,8 @@ class ResultsTranslate(MainTranslate): "discharge": self._dict["unit_discharge"], "velocity": self._dict["unit_speed"], "width": self._dict["unit_width"], - "depth": self._dict["unit_height"], + "max_depth": self._dict["unit_max_height"], + "mean_depth": self._dict["unit_mean_height"], "wet_area": self._dict["unit_wet_area"], "wet_perimeter": self._dict["unit_wet_perimeter"], "hydraulic_radius": self._dict["unit_hydraulic_radius"], diff --git a/src/View/Translate.py b/src/View/Translate.py index a9c3d8ee..5df0e944 100644 --- a/src/View/Translate.py +++ b/src/View/Translate.py @@ -54,7 +54,9 @@ class UnitTranslate(CommonWordTranslate): self._dict["unit_rk"] = _translate("Unit", "River Kilometric (m)") self._dict["unit_width"] = _translate("Unit", "Width (m)") - self._dict["unit_height"] = _translate("Unit", "Height (m)") + self._dict["unit_height"] = _translate("Unit", "Depth (m)") + self._dict["unit_max_height"] = _translate("Unit", "Max Depth (m)") + self._dict["unit_mean_height"] = _translate("Unit", "Mean Depth (m)") self._dict["unit_diameter"] = _translate("Unit", "Diameter (m)") self._dict["unit_thickness"] = _translate("Unit", "Thickness (m)") self._dict["unit_elevation"] = _translate("Unit", "Elevation (m)") From 80a463a91e2a9b4861d33274d29c18ba87f390a2 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Tue, 30 Jul 2024 14:19:42 +0200 Subject: [PATCH 05/29] pep8 --- src/Model/Geometry/ProfileXYZ.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index c06a2653..27b933b5 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -408,11 +408,10 @@ class ProfileXYZ(Profile, SQLSubModel): return 0 length = 0.0 - for s,e in zip(start, end): + for s, e in zip(start, end): length += abs(s - e) return length - def wet_perimeter(self, z): lines = self.wet_lines(z) @@ -477,10 +476,10 @@ class ProfileXYZ(Profile, SQLSubModel): [zz[i], zz[i+1]], [station[i], station[i+1]] ) - line.append([y,z]) + line.append([y, z]) if zz[i] < z: - line.append([station[i],zz[i]]) + line.append([station[i], zz[i]]) if zz[i] <= z and zz[i+1] > z: y = np.interp( @@ -488,7 +487,7 @@ class ProfileXYZ(Profile, SQLSubModel): [zz[i], zz[i+1]], [station[i], station[i+1]] ) - line.append([y,z]) + line.append([y, z]) lines.append(geometry.LineString(line)) line = [] From 5e018fc8f068f492200ff1f1aff8ed6f82e38cfc Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Tue, 30 Jul 2024 16:32:07 +0200 Subject: [PATCH 06/29] lighter data export --- src/View/Results/Window.py | 60 +++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index ac1f3585..146847ae 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -303,7 +303,8 @@ class ResultsWindow(PamhyrWindow): actions = { "action_reload": self._reload, "action_add": self._add_custom_plot, - "action_export": self.export, + # "action_export": self.export, + "action_export": self.export_current, } for action in actions: @@ -591,12 +592,15 @@ class ResultsWindow(PamhyrWindow): ) def export_to(self, directory): + timestamps = sorted(self._results.get("timestamps")) for reach in self._results.river.reachs: - self.export_reach(reach, directory) + self.export_reach(reach, directory, timestamps) - def export_reach(self, reach, directory): + def export_reach(self, reach, directory, timestamps): name = reach.name name = name.replace(" ", "-") + if len(timestamps) == 1: + name = f"{name}_t{timestamps[0]}" file_name = os.path.join( directory, @@ -606,28 +610,40 @@ class ResultsWindow(PamhyrWindow): with open(file_name, 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) - writer.writerow(["name", "rk", "data-file"]) - for profile in reach.profiles: - p_file_name = os.path.join( - directory, - f"cs_{profile.geometry.id}.csv" - ) + 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 - ]) + writer.writerow([ + profile.name, + profile.rk, + p_file_name + ]) - self.export_profile(reach, profile, 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): + 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"]) - timestamps = sorted(self._results.get("timestamps")) for ts in timestamps: writer.writerow([ @@ -635,3 +651,13 @@ class ResultsWindow(PamhyrWindow): profile.get_ts_key(ts, "Z"), profile.get_ts_key(ts, "Q"), ]) + + def export_current(self): + self.file_dialog( + select_file=False, + 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()]) From d32baa61a1be8638cd43935293929562cee84ece Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Mon, 5 Aug 2024 09:55:12 +0200 Subject: [PATCH 07/29] text improvement --- doc/dev/documentation.org | 2 +- src/Model/Geometry/ProfileXYZ.py | 5 +++-- src/View/ui/InitialConditions.ui | 4 ++-- src/lang/fr.ts | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/dev/documentation.org b/doc/dev/documentation.org index 28fad839..f7285945 100644 --- a/doc/dev/documentation.org +++ b/doc/dev/documentation.org @@ -1334,7 +1334,7 @@ https://gitlab.irstea.fr/theophile.terraz/pamhyr You can improve or add translation for the project. To contribute to Pamhyr2 translate, you need to use Qt Linguist[fn:qt-linguist]. Open -Qt-linguist and edite the translation ({{{file(.ts)}}}) file, finally, +Qt-linguist and edit the translation ({{{file(.ts)}}}) file, finally, commit the new version of file and make a merge request. If you want add a new language, edit the script diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 27b933b5..54626bb3 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -431,8 +431,9 @@ class ProfileXYZ(Profile, SQLSubModel): area = 0.0 for line in lines: - poly = geometry.Polygon(line) - area += poly.area + if len(line.coords) > 2: + poly = geometry.Polygon(line) + area += poly.area return area return poly.area diff --git a/src/View/ui/InitialConditions.ui b/src/View/ui/InitialConditions.ui index 403a5fe2..68ed2f94 100644 --- a/src/View/ui/InitialConditions.ui +++ b/src/View/ui/InitialConditions.ui @@ -27,14 +27,14 @@ - Generate from height + Generate height - Generate from discharge + Generate discharge diff --git a/src/lang/fr.ts b/src/lang/fr.ts index 81b4d312..7ae74e51 100644 --- a/src/lang/fr.ts +++ b/src/lang/fr.ts @@ -2168,13 +2168,13 @@ - Generate from height - Générer pour une hauteur donnée + Generate height + Générer une hauteur - Generate from discharge - Générer pour un débit donné + Generate discharge + Générer un débit From a698d461441eee1df2ac48805b77e11ac6c68185 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Mon, 5 Aug 2024 11:10:43 +0200 Subject: [PATCH 08/29] debug water limits --- src/Model/Geometry/ProfileXYZ.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 54626bb3..d3e98097 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -565,18 +565,18 @@ class ProfileXYZ(Profile, SQLSubModel): start.append(y) end = [] - for i in reversed(range(self.number_points-1)): - if zz[i-1] <= z and zz[i] > z: - y = np.interp( - z, - [zz[i-1], zz[i]], - [station[i-1], station[i]] - ) - end.append(y) - if points[-1].z <= z: end.append(station[-1]) + for i in reversed(range(self.number_points-1)): + if zz[i] <= z and zz[i+1] > z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + end.append(y) + if len(start) != len(end): logger.error(f"ERROR in get_all_water_limits_ac") return [], [] From 12cca24d8b4fe71a35aefca9550e6a497895175a Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Mon, 5 Aug 2024 15:50:22 +0200 Subject: [PATCH 09/29] add shift button in geometry window --- src/Model/Geometry/ProfileXYZ.py | 6 + src/View/Geometry/ShiftDialog.py | 60 ++++++++ src/View/Geometry/Table.py | 9 ++ src/View/Geometry/Translate.py | 9 ++ src/View/Geometry/UndoCommand.py | 29 ++++ src/View/Geometry/Window.py | 28 +++- src/View/InitialConditions/Window.py | 2 +- src/View/ui/GeometryReach.ui | 9 ++ src/View/ui/GeometryReachShift.ui | 134 ++++++++++++++++++ ...{UpdateKPOptions.ui => UpdateRKOptions.ui} | 0 10 files changed, 281 insertions(+), 5 deletions(-) create mode 100644 src/View/Geometry/ShiftDialog.py create mode 100644 src/View/ui/GeometryReachShift.ui rename src/View/ui/{UpdateKPOptions.ui => UpdateRKOptions.ui} (100%) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index d3e98097..0e29c8c3 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -779,3 +779,9 @@ class ProfileXYZ(Profile, SQLSubModel): self.point(i), self.point(i+1) ) + + def shift(self, x, y, z): + for p in self.points: + p.x = p.x + x + p.y = p.y + y + p.z = p.z + z diff --git a/src/View/Geometry/ShiftDialog.py b/src/View/Geometry/ShiftDialog.py new file mode 100644 index 00000000..4953855a --- /dev/null +++ b/src/View/Geometry/ShiftDialog.py @@ -0,0 +1,60 @@ +# ShiftDialog.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 + + +class ShiftDialog(PamhyrDialog): + _pamhyr_ui = "GeometryReachShift" + _pamhyr_name = "Shift" + + def __init__(self, trad=None, parent=None): + super(ShiftDialog, self).__init__( + title=trad[self._pamhyr_name], + trad=trad, + options=[], + parent=parent + ) + + self._init_default_values() + + def _init_default_values(self): + self._dx = 0.0 + self._dy = 0.0 + self._dz = 0.0 + + @property + def dx(self): + return self._dx + + @property + def dy(self): + return self._dy + + @property + def dz(self): + return self._dz + + def accept(self): + self._dx = self.get_double_spin_box("doubleSpinBox_X") + self._dy = self.get_double_spin_box("doubleSpinBox_Y") + self._dz = self.get_double_spin_box("doubleSpinBox_Z") + super().accept() + + def reject(self): + self.close() diff --git a/src/View/Geometry/Table.py b/src/View/Geometry/Table.py index 2676037d..fd91707a 100644 --- a/src/View/Geometry/Table.py +++ b/src/View/Geometry/Table.py @@ -267,3 +267,12 @@ class GeometryReachTableModel(PamhyrTableModel): ) ) self.layoutChanged.emit() + + def shift(self, rows, dx, dy, dz): + + self._undo.push( + ShiftCommand( + self._data, rows, dx, dy, dz + ) + ) + self.layoutChanged.emit() diff --git a/src/View/Geometry/Translate.py b/src/View/Geometry/Translate.py index b2bc2eec..7944d661 100644 --- a/src/View/Geometry/Translate.py +++ b/src/View/Geometry/Translate.py @@ -82,3 +82,12 @@ class GeometryTranslate(MainTranslate): self._dict["Meshing"] = _translate( "Geometry", "Meshing" ) + self._dict["UpdateRK"] = _translate( + "Geometry", "UpdateRK" + ) + self._dict["Purge"] = _translate( + "Geometry", "Purge" + ) + self._dict["Shift"] = _translate( + "Geometry", "Shift" + ) diff --git a/src/View/Geometry/UndoCommand.py b/src/View/Geometry/UndoCommand.py index 772c84e1..d3cc29ef 100644 --- a/src/View/Geometry/UndoCommand.py +++ b/src/View/Geometry/UndoCommand.py @@ -281,3 +281,32 @@ class PurgeCommand(QUndoCommand): def redo(self): for profile in self._reach._profiles: profile.purge(self._np_purge) + + +class ShiftCommand(QUndoCommand): + def __init__(self, reach, rows, dx, dy, dz): + QUndoCommand.__init__(self) + + self._reach = reach + self._rows = rows + self._dx = dx + self._dy = dy + self._dz = dz + + self._old = [] + for profile in self._reach.profiles: + self._old.append(profile.points.copy()) + + def undo(self): + for i in self._rows: + profile = self._reach.profiles[i] + self._reach.profiles[i].shift(-self._dx, + -self._dy, + -self._dz) + + def redo(self): + for i in self._rows: + profile = self._reach.profiles[i] + self._reach.profiles[i].shift(self._dx, + self._dy, + self._dz) diff --git a/src/View/Geometry/Window.py b/src/View/Geometry/Window.py index 20fe4aa2..1a621808 100644 --- a/src/View/Geometry/Window.py +++ b/src/View/Geometry/Window.py @@ -58,6 +58,7 @@ from View.Geometry.PlotRKZ import PlotRKZ from View.Geometry.MeshingDialog import MeshingDialog from View.Geometry.UpdateRKDialog import UpdateRKDialog from View.Geometry.PurgeDialog import PurgeDialog +from View.Geometry.ShiftDialog import ShiftDialog from View.Geometry.Translate import GeometryTranslate from View.Geometry.Profile.Window import ProfileWindow @@ -194,6 +195,7 @@ class GeometryWindow(PamhyrWindow): "action_meshing": self.edit_meshing, "action_update_rk": self.update_rk, "action_purge": self.purge, + "action_shift": self.shift, } for action in actions: @@ -499,10 +501,6 @@ class GeometryWindow(PamhyrWindow): self._table.move_down(row) self.select_current_profile() - def purge(self): - self._table.purge() - self.update_redraw() - def purge(self): try: dlg = PurgeDialog( @@ -515,6 +513,28 @@ class GeometryWindow(PamhyrWindow): logger_exception(e) return + def shift(self): + rows = sorted( + list( + set( + [index.row() for index in self.tableView.selectedIndexes()] + ) + ) + ) + try: + dlg = ShiftDialog( + trad=self._trad, + parent=self + ) + if dlg.exec(): + self._table.shift(rows, + dlg.dx, + dlg.dy, + dlg.dz) + except Exception as e: + logger_exception(e) + return + def duplicate(self): rows = [ row.row() for row in diff --git a/src/View/InitialConditions/Window.py b/src/View/InitialConditions/Window.py index 1113b121..e6563169 100644 --- a/src/View/InitialConditions/Window.py +++ b/src/View/InitialConditions/Window.py @@ -194,7 +194,7 @@ class InitialConditionsWindow(PamhyrWindow): return rows[0].row() def update(self): - self.update(propagate=False) + self._update(propagate=False) def _update(self, propagate=True): self._update_plot() diff --git a/src/View/ui/GeometryReach.ui b/src/View/ui/GeometryReach.ui index 97be0806..2d175bdc 100644 --- a/src/View/ui/GeometryReach.ui +++ b/src/View/ui/GeometryReach.ui @@ -92,6 +92,7 @@ + @@ -226,6 +227,14 @@ Purge cross-sections to keep a given number of points + + + Shift + + + Shift selected sections coordinates + + diff --git a/src/View/ui/GeometryReachShift.ui b/src/View/ui/GeometryReachShift.ui new file mode 100644 index 00000000..575dbb98 --- /dev/null +++ b/src/View/ui/GeometryReachShift.ui @@ -0,0 +1,134 @@ + + + Dialog + + + + 0 + 0 + 381 + 144 + + + + Dialog + + + Qt::LeftToRight + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Y coordinate + + + + + + + X coordinate + + + + + + + 4 + + + -99999999.000000000000000 + + + 99999999.000000000000000 + + + + + + + 4 + + + -99999999.000000000000000 + + + 99999999.000000000000000 + + + + + + + true + + + Z coordinate + + + + + + + 4 + + + -99999999.000000000000000 + + + 99999999.000000000000000 + + + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/View/ui/UpdateKPOptions.ui b/src/View/ui/UpdateRKOptions.ui similarity index 100% rename from src/View/ui/UpdateKPOptions.ui rename to src/View/ui/UpdateRKOptions.ui From f96f9cc62fb7c172353ecf011fda3a0b15c5b418 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Mon, 5 Aug 2024 18:30:22 +0200 Subject: [PATCH 10/29] work on custom plots --- src/View/Results/CustomPlot/Plot.py | 108 ++++++++++++++++++----- src/View/Results/CustomPlot/Translate.py | 3 + src/View/Results/Window.py | 12 ++- src/View/Tools/PamhyrPlot.py | 2 - src/View/Tools/Plot/PamhyrCanvas.py | 3 +- src/View/ui/Results.ui | 5 +- 6 files changed, 103 insertions(+), 30 deletions(-) diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index e45d9ea1..b0107a1d 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -32,6 +32,7 @@ unit = { "elevation": "0-meter", "water_elevation": "0-meter", "discharge": "1-m3s", + "velocity": "2-ms", } @@ -76,18 +77,40 @@ class CustomPlot(PamhyrPlot): reach = results.river.reach(self._reach) rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() + q = list( + map( + lambda p: p.get_ts_key(self._timestamp, "Q"), + reach.profiles + ) + ) + z = list( + map( + lambda p: p.get_ts_key(self._timestamp, "Z"), + reach.profiles + ) + ) # self.canvas.axes.set_xlim( # left=min(rk), right=max(rk) # ) meter_axes = self.canvas.axes - m3S_axes = self.canvas.axes + #m3s_axes = meter_axes.twinx() + #ms_axes = meter_axes.twinx() + shift = 0 if "0-meter" in self._y_axes and "1-m3s" in self._y_axes: m3s_axes = self._axes["1-m3s"] + m3s_axes.spines['right'].set_position(('outward', shift)) + shift += 60 + + if "0-meter" in self._y_axes and "2-ms" in self._y_axes: + ms_axes = self._axes["2-ms"] + ms_axes.spines['right'].set_position(('outward', shift)) + shift += 60 lines = {} if "elevation" in self._y: + # meter_axes.set_ylim( # bottom=min(0, min(z_min)), # top=max(z_min) + 1 @@ -100,38 +123,25 @@ class CustomPlot(PamhyrPlot): lines["elevation"] = line if "water_elevation" in self._y: - # Water elevation - water_z = list( - map( - lambda p: p.get_ts_key(self._timestamp, "Z"), - reach.profiles - ) - ) # meter_axes.set_ylim( # bottom=min(0, min(z_min)), - # top=max(water_z) + 1 + # top=max(z) + 1 # ) line = meter_axes.plot( - rk, water_z, lw=1., + rk, z, lw=1., color='blue', ) lines["water_elevation"] = line if "elevation" in self._y: meter_axes.fill_between( - rk, z_min, water_z, + rk, z_min, z, color='blue', alpha=0.5, interpolate=True ) if "discharge" in self._y: - q = list( - map( - lambda p: p.get_ts_key(self._timestamp, "Q"), - reach.profiles - ) - ) # m3s_axes.set_ylim( # bottom=min(0, min(q)), @@ -144,6 +154,27 @@ class CustomPlot(PamhyrPlot): ) lines["discharge"] = line + if "velocity" in self._y: + v = list( + map( + lambda p: p.geometry.speed( + p.get_ts_key(self._timestamp, "Q"), + p.get_ts_key(self._timestamp, "Z")), + reach.profiles + ) + ) + + # m3s_axes.set_ylim( + # bottom=min(0, min(q)), + # top=max(q) + 1 + # ) + + line = ms_axes.plot( + rk, v, lw=1., + color='g', + ) + lines["velocity"] = line + # Legend lns = reduce( lambda acc, line: acc + line, @@ -151,7 +182,7 @@ class CustomPlot(PamhyrPlot): [] ) labs = list(map(lambda line: self._trad[line], lines)) - self.canvas.axes.legend(lns, labs, loc="lower left") + self.canvas.axes.legend(lns, labs, loc="best") def _customize_x_axes_time(self, ts, mode="time"): # Custom time display @@ -199,13 +230,25 @@ class CustomPlot(PamhyrPlot): profile = reach.profile(self._profile) meter_axes = self.canvas.axes - m3S_axes = self.canvas.axes + #m3s_axes = meter_axes.twinx() + #ms_axes = meter_axes.twinx() + shift = 0 if "0-meter" in self._y_axes and "1-m3s" in self._y_axes: m3s_axes = self._axes["1-m3s"] + m3s_axes.spines['right'].set_position(('outward', shift)) + shift += 60 + + if "0-meter" in self._y_axes and "2-ms" in self._y_axes: + ms_axes = self._axes["2-ms"] + ms_axes.spines['right'].set_position(('outward', shift)) + shift += 60 ts = list(results.get("timestamps")) ts.sort() + q = profile.get_key("Q") + z = profile.get_key("Z") + # self.canvas.axes.set_xlim( # left=min(ts), right=max(ts) # ) @@ -229,8 +272,6 @@ class CustomPlot(PamhyrPlot): lines["elevation"] = line if "water_elevation" in self._y: - # Water elevation - z = profile.get_key("Z") # meter_axes.set_ylim( # bottom=min(0, min(z)), @@ -258,7 +299,6 @@ class CustomPlot(PamhyrPlot): ) if "discharge" in self._y: - q = profile.get_key("Q") # m3s_axes.set_ylim( # bottom=min(0, min(q)), @@ -271,6 +311,26 @@ class CustomPlot(PamhyrPlot): ) lines["discharge"] = line + if "velocity" in self._y: + + v = list( + map( + lambda q, z: profile.geometry.speed(q, z), + q, z + ) + ) + + # ms_axes.set_ylim( + # bottom=min(0, min(q)), + # top=max(q) + 1 + # ) + + line = ms_axes.plot( + ts, v, lw=1., + color='g', + ) + lines["velocity"] = line + self._customize_x_axes_time(ts) # Legend @@ -280,7 +340,7 @@ class CustomPlot(PamhyrPlot): [] ) labs = list(map(lambda line: self._trad[line], lines)) - self.canvas.axes.legend(lns, labs, loc="lower left") + self.canvas.axes.legend(lns, labs, loc="best") @timer def draw(self): @@ -317,7 +377,7 @@ class CustomPlot(PamhyrPlot): elif self._x == "time": self._draw_time() - self.canvas.figure.tight_layout() + #self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() if self.toolbar is not None: self.toolbar.update() diff --git a/src/View/Results/CustomPlot/Translate.py b/src/View/Results/CustomPlot/Translate.py index c32d2b58..ebbea20a 100644 --- a/src/View/Results/CustomPlot/Translate.py +++ b/src/View/Results/CustomPlot/Translate.py @@ -40,6 +40,7 @@ class CustomPlotTranslate(ResultsTranslate): self._dict['elevation'] = _translate( "CustomPlot", "Bed elevation (m)" ) + self._dict['velocity'] = self._dict["unit_speed"] # Unit corresponding long name (plot axes display) @@ -47,6 +48,7 @@ class CustomPlotTranslate(ResultsTranslate): "CustomPlot", "Bed elevation (m)" ) self._dict['1-m3s'] = self._dict["unit_discharge"] + self._dict['2-ms'] = self._dict["unit_speed"] # SubDict @@ -58,4 +60,5 @@ class CustomPlotTranslate(ResultsTranslate): "elevation": self._dict["elevation"], "water_elevation": self._dict["water_elevation"], "discharge": self._dict["discharge"], + "velocity": self._dict["velocity"], } diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 146847ae..3577b21a 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -40,7 +40,7 @@ from PyQt5.QtWidgets import ( QFileDialog, QTableView, QAbstractItemView, QUndoStack, QShortcut, QAction, QItemDelegate, QComboBox, QVBoxLayout, QHeaderView, QTabWidget, - QSlider, QLabel, QWidget, QGridLayout, + QSlider, QLabel, QWidget, QGridLayout, QTabBar ) from View.Tools.Plot.PamhyrCanvas import MplCanvas @@ -158,6 +158,12 @@ class ResultsWindow(PamhyrWindow): def setup_plots(self): self.canvas = MplCanvas(width=5, height=4, dpi=100) + tab_widget = self.find(QTabWidget, f"tabWidget") + tab_widget.setTabsClosable(True) + tab_widget.tabCloseRequested.connect(self.delete_tab) + tab_widget.tabBar().setTabButton(0, QTabBar.RightSide, None) + tab_widget.tabBar().setTabButton(1, QTabBar.RightSide, None) + tab_widget.tabBar().setTabButton(2, QTabBar.RightSide, None) self.canvas.setObjectName("canvas") self.toolbar = PamhyrPlotToolbar( self.canvas, self, items=[ @@ -661,3 +667,7 @@ class ResultsWindow(PamhyrWindow): def export_current_to(self, directory): reach = self._results.river.reachs[self._get_current_reach()] self.export_reach(reach, directory, [self._get_current_timestamp()]) + + def delete_tab(self, index): + tab_widget = self.find(QTabWidget, f"tabWidget") + tab_widget.removeTab(index) diff --git a/src/View/Tools/PamhyrPlot.py b/src/View/Tools/PamhyrPlot.py index 76159205..b893db84 100644 --- a/src/View/Tools/PamhyrPlot.py +++ b/src/View/Tools/PamhyrPlot.py @@ -192,7 +192,6 @@ class PamhyrPlot(APlot): self.canvas.axes.autoscale_view(True, True, True) self.canvas.axes.autoscale() - self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() self.toolbar_update() @@ -205,7 +204,6 @@ class PamhyrPlot(APlot): self.canvas.axes.autoscale_view(True, True, True) self.canvas.axes.autoscale() - self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() self.toolbar_update() diff --git a/src/View/Tools/Plot/PamhyrCanvas.py b/src/View/Tools/Plot/PamhyrCanvas.py index 1694d1fd..2101aa60 100644 --- a/src/View/Tools/Plot/PamhyrCanvas.py +++ b/src/View/Tools/Plot/PamhyrCanvas.py @@ -23,7 +23,7 @@ class MplCanvas(FigureCanvasQTAgg): fig = Figure( figsize=(width, height), dpi=dpi, - layout='tight', + layout='constrained', ) super(MplCanvas, self).__init__(fig) @@ -36,7 +36,6 @@ class MplCanvas(FigureCanvasQTAgg): self.axes.yaxis.tick_left() self.axes.xaxis.tick_bottom() self.axes.spines[['top', 'right']].set_color('none') - self.figure.tight_layout() self.add_arrows() def add_arrows(self): diff --git a/src/View/ui/Results.ui b/src/View/ui/Results.ui index 9bbd4580..479f5f05 100644 --- a/src/View/ui/Results.ui +++ b/src/View/ui/Results.ui @@ -145,7 +145,10 @@ - 1 + 0 + + + true From e823b3ae95db4a028e79214fad0341310e44daec Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Tue, 6 Aug 2024 16:00:16 +0200 Subject: [PATCH 11/29] work on custom plots --- src/View/Results/CustomPlot/Plot.py | 116 ++++++++--------------- src/View/Results/CustomPlot/Translate.py | 6 ++ src/View/Results/Window.py | 5 +- 3 files changed, 44 insertions(+), 83 deletions(-) diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index b0107a1d..e8a21d95 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -90,33 +90,21 @@ class CustomPlot(PamhyrPlot): ) ) - # self.canvas.axes.set_xlim( - # left=min(rk), right=max(rk) - # ) - - meter_axes = self.canvas.axes - #m3s_axes = meter_axes.twinx() - #ms_axes = meter_axes.twinx() shift = 0 - if "0-meter" in self._y_axes and "1-m3s" in self._y_axes: - m3s_axes = self._axes["1-m3s"] - m3s_axes.spines['right'].set_position(('outward', shift)) - shift += 60 - - if "0-meter" in self._y_axes and "2-ms" in self._y_axes: - ms_axes = self._axes["2-ms"] - ms_axes.spines['right'].set_position(('outward', shift)) - shift += 60 + compt = 0 + for ax in sorted(self._axes): + if compt == 0: + self._axes[ax].spines['left'].set_position(('outward', shift)) + compt += 1 + else: + self._axes[ax].spines['right'].set_position(('outward', shift)) + shift += 60 lines = {} if "elevation" in self._y: - # meter_axes.set_ylim( - # bottom=min(0, min(z_min)), - # top=max(z_min) + 1 - # ) - - line = meter_axes.plot( + ax = self._axes[unit["elevation"]] + line = ax.plot( rk, z_min, color='grey', lw=1., ) @@ -124,37 +112,31 @@ class CustomPlot(PamhyrPlot): if "water_elevation" in self._y: - # meter_axes.set_ylim( - # bottom=min(0, min(z_min)), - # top=max(z) + 1 - # ) - - line = meter_axes.plot( + ax = self._axes[unit["water_elevation"]] + line = ax.plot( rk, z, lw=1., color='blue', ) lines["water_elevation"] = line if "elevation" in self._y: - meter_axes.fill_between( + ax.fill_between( rk, z_min, z, color='blue', alpha=0.5, interpolate=True ) if "discharge" in self._y: - # m3s_axes.set_ylim( - # bottom=min(0, min(q)), - # top=max(q) + 1 - # ) - - line = m3s_axes.plot( + ax = self._axes[unit["discharge"]] + line = ax.plot( rk, q, lw=1., color='r', ) lines["discharge"] = line if "velocity" in self._y: + + ax = self._axes[unit["velocity"]] v = list( map( lambda p: p.geometry.speed( @@ -164,12 +146,7 @@ class CustomPlot(PamhyrPlot): ) ) - # m3s_axes.set_ylim( - # bottom=min(0, min(q)), - # top=max(q) + 1 - # ) - - line = ms_axes.plot( + line = ax.plot( rk, v, lw=1., color='g', ) @@ -229,19 +206,15 @@ class CustomPlot(PamhyrPlot): reach = results.river.reach(self._reach) profile = reach.profile(self._profile) - meter_axes = self.canvas.axes - #m3s_axes = meter_axes.twinx() - #ms_axes = meter_axes.twinx() shift = 0 - if "0-meter" in self._y_axes and "1-m3s" in self._y_axes: - m3s_axes = self._axes["1-m3s"] - m3s_axes.spines['right'].set_position(('outward', shift)) - shift += 60 - - if "0-meter" in self._y_axes and "2-ms" in self._y_axes: - ms_axes = self._axes["2-ms"] - ms_axes.spines['right'].set_position(('outward', shift)) - shift += 60 + compt = 0 + for ax in sorted(self._axes): + if compt == 0: + self._axes[ax].spines['left'].set_position(('outward', shift)) + compt += 1 + else: + self._axes[ax].spines['right'].set_position(('outward', shift)) + shift += 60 ts = list(results.get("timestamps")) ts.sort() @@ -249,14 +222,11 @@ class CustomPlot(PamhyrPlot): q = profile.get_key("Q") z = profile.get_key("Z") - # self.canvas.axes.set_xlim( - # left=min(ts), right=max(ts) - # ) - - x = ts lines = {} if "elevation" in self._y: # Z min is constant in time + + ax = self._axes[unit["elevation"]] z_min = profile.geometry.z_min() ts_z_min = list( map( @@ -265,7 +235,7 @@ class CustomPlot(PamhyrPlot): ) ) - line = meter_axes.plot( + line = ax.plot( ts, ts_z_min, color='grey', lw=1. ) @@ -273,12 +243,8 @@ class CustomPlot(PamhyrPlot): if "water_elevation" in self._y: - # meter_axes.set_ylim( - # bottom=min(0, min(z)), - # top=max(z) + 1 - # ) - - line = meter_axes.plot( + ax = self._axes[unit["water_elevation"]] + line = ax.plot( ts, z, lw=1., color='b', ) @@ -293,19 +259,15 @@ class CustomPlot(PamhyrPlot): ) ) - meter_axes.fill_between( + ax.fill_between( ts, ts_z_min, z, color='blue', alpha=0.5, interpolate=True ) if "discharge" in self._y: - # m3s_axes.set_ylim( - # bottom=min(0, min(q)), - # top=max(q) + 1 - # ) - - line = m3s_axes.plot( + ax = self._axes[unit["discharge"]] + line = ax.plot( ts, q, lw=1., color='r', ) @@ -313,6 +275,7 @@ class CustomPlot(PamhyrPlot): if "velocity" in self._y: + ax = self._axes[unit["velocity"]] v = list( map( lambda q, z: profile.geometry.speed(q, z), @@ -320,12 +283,7 @@ class CustomPlot(PamhyrPlot): ) ) - # ms_axes.set_ylim( - # bottom=min(0, min(q)), - # top=max(q) + 1 - # ) - - line = ms_axes.plot( + line = ax.plot( ts, v, lw=1., color='g', ) @@ -360,6 +318,7 @@ class CustomPlot(PamhyrPlot): color='black', fontsize=10 ) + self._axes[self._y_axes[0]] = self.canvas.axes for axes in self._y_axes[1:]: if axes in self._axes: self._axes[axes].clear() @@ -377,7 +336,6 @@ class CustomPlot(PamhyrPlot): elif self._x == "time": self._draw_time() - #self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() if self.toolbar is not None: self.toolbar.update() diff --git a/src/View/Results/CustomPlot/Translate.py b/src/View/Results/CustomPlot/Translate.py index ebbea20a..5e9944c7 100644 --- a/src/View/Results/CustomPlot/Translate.py +++ b/src/View/Results/CustomPlot/Translate.py @@ -41,6 +41,12 @@ class CustomPlotTranslate(ResultsTranslate): "CustomPlot", "Bed elevation (m)" ) self._dict['velocity'] = self._dict["unit_speed"] + self._dict['width'] = self._dict["unit_width"], + self._dict['max_depth'] = self._dict["unit_max_height"], + self._dict['mean_depth'] = self._dict["unit_mean_height"], + self._dict['wet_area'] = self._dict["unit_wet_area"], + self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"], + self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"], # Unit corresponding long name (plot axes display) diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 3577b21a..dc47d0a5 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -507,7 +507,7 @@ class ResultsWindow(PamhyrWindow): tab_widget = self.find(QTabWidget, f"tabWidget") # This plot already exists - if name in self._additional_plot: + if name in [tab_widget.tabText(i) for i in range(tab_widget.count())]: tab_widget.setCurrentWidget( tab_widget.findChild(QWidget, wname) ) @@ -536,9 +536,6 @@ class ResultsWindow(PamhyrWindow): ) plot.draw() - # Add plot to additional plot - self._additional_plot[name] = plot - grid.addWidget(toolbar, 0, 0) grid.addWidget(canvas, 1, 0) widget.setLayout(grid) From 271f977cddaf336cd28f3332ff706d34beb698fd Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 7 Aug 2024 09:34:53 +0200 Subject: [PATCH 12/29] kilometer instead of kilometric --- src/View/Translate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Translate.py b/src/View/Translate.py index 5df0e944..1a47f238 100644 --- a/src/View/Translate.py +++ b/src/View/Translate.py @@ -52,7 +52,7 @@ class UnitTranslate(CommonWordTranslate): def __init__(self): super(UnitTranslate, self).__init__() - self._dict["unit_rk"] = _translate("Unit", "River Kilometric (m)") + self._dict["unit_rk"] = _translate("Unit", "River Kilometer (m)") self._dict["unit_width"] = _translate("Unit", "Width (m)") self._dict["unit_height"] = _translate("Unit", "Depth (m)") self._dict["unit_max_height"] = _translate("Unit", "Max Depth (m)") From d19506f1adbe860d8376ad04e128324a3579dbae Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 7 Aug 2024 11:32:47 +0200 Subject: [PATCH 13/29] debug table default timestep --- src/View/Results/Window.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index dc47d0a5..726483e0 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -108,9 +108,9 @@ class ResultsWindow(PamhyrWindow): try: self._timestamps = sorted(list(self._results.get("timestamps"))) + self.setup_slider() self.setup_table() self.setup_plots() - self.setup_slider() self.setup_statusbar() self.setup_connections() except Exception as e: @@ -128,6 +128,7 @@ class ResultsWindow(PamhyrWindow): undo=self._undo_stack, opt_data=t ) + self._table[t]._timestamp = self._timestamps[self._slider_time.value()] def setup_slider(self): self._slider_profile = self.find(QSlider, f"verticalSlider_profile") From fb62298bd2ba0fcdb78aa8e108e973f90126ee7d Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 7 Aug 2024 11:33:41 +0200 Subject: [PATCH 14/29] more custom plots --- src/View/Results/CustomPlot/Plot.py | 82 ++++++++++++++++++++---- src/View/Results/CustomPlot/Translate.py | 15 +++-- src/lang/fr.ts | 2 +- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index e8a21d95..f9ebdfcf 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -33,6 +33,8 @@ unit = { "water_elevation": "0-meter", "discharge": "1-m3s", "velocity": "2-ms", + "depth": "3-meter", + "mean_depth": "3-meter", } @@ -152,6 +154,39 @@ class CustomPlot(PamhyrPlot): ) lines["velocity"] = line + if "depth" in self._y: + + ax = self._axes[unit["depth"]] + d = list( + map( + lambda p: p.geometry.max_water_depth( + p.get_ts_key(self._timestamp, "Z")), + reach.profiles + ) + ) + line = ax.plot( + rk, d, + color='brown', lw=1., + ) + lines["depth"] = line + + if "mean_depth" in self._y: + + ax = self._axes[unit["depth"]] + d = list( + map( + lambda p: p.geometry.mean_water_depth( + p.get_ts_key(self._timestamp, "Z")), + reach.profiles + ) + ) + + line = ax.plot( + rk, d, + color='orange', lw=1., + ) + lines["depth"] = line + # Legend lns = reduce( lambda acc, line: acc + line, @@ -221,19 +256,19 @@ class CustomPlot(PamhyrPlot): q = profile.get_key("Q") z = profile.get_key("Z") + z_min = profile.geometry.z_min() + ts_z_min = list( + map( + lambda ts: z_min, + ts + ) + ) lines = {} if "elevation" in self._y: # Z min is constant in time ax = self._axes[unit["elevation"]] - z_min = profile.geometry.z_min() - ts_z_min = list( - map( - lambda ts: z_min, - ts - ) - ) line = ax.plot( ts, ts_z_min, @@ -251,13 +286,6 @@ class CustomPlot(PamhyrPlot): lines["water_elevation"] = line if "elevation" in self._y: - z_min = profile.geometry.z_min() - ts_z_min = list( - map( - lambda ts: z_min, - ts - ) - ) ax.fill_between( ts, ts_z_min, z, @@ -289,6 +317,32 @@ class CustomPlot(PamhyrPlot): ) lines["velocity"] = line + if "depth" in self._y: + + ax = self._axes[unit["depth"]] + d = list( + map(lambda z: profile.geometry.max_water_depth(z), z) + ) + + line = ax.plot( + ts, d, + color='brown', lw=1., + ) + lines["depth"] = line + + if "mean_depth" in self._y: + + ax = self._axes[unit["depth"]] + d = list( + map(lambda z: profile.geometry.mean_water_depth(z), z) + ) + + line = ax.plot( + ts, d, + color='orange', lw=1., + ) + lines["depth"] = line + self._customize_x_axes_time(ts) # Legend diff --git a/src/View/Results/CustomPlot/Translate.py b/src/View/Results/CustomPlot/Translate.py index 5e9944c7..df4ad75b 100644 --- a/src/View/Results/CustomPlot/Translate.py +++ b/src/View/Results/CustomPlot/Translate.py @@ -41,12 +41,12 @@ class CustomPlotTranslate(ResultsTranslate): "CustomPlot", "Bed elevation (m)" ) self._dict['velocity'] = self._dict["unit_speed"] - self._dict['width'] = self._dict["unit_width"], - self._dict['max_depth'] = self._dict["unit_max_height"], - self._dict['mean_depth'] = self._dict["unit_mean_height"], - self._dict['wet_area'] = self._dict["unit_wet_area"], - self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"], - self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"], + self._dict['width'] = self._dict["unit_width"] + self._dict['max_depth'] = self._dict["unit_max_height"] + self._dict['mean_depth'] = self._dict["unit_mean_height"] + self._dict['wet_area'] = self._dict["unit_wet_area"] + self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"] + self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"] # Unit corresponding long name (plot axes display) @@ -55,6 +55,7 @@ class CustomPlotTranslate(ResultsTranslate): ) self._dict['1-m3s'] = self._dict["unit_discharge"] self._dict['2-ms'] = self._dict["unit_speed"] + self._dict['3-meter'] = self._dict["unit_height"] # SubDict @@ -67,4 +68,6 @@ class CustomPlotTranslate(ResultsTranslate): "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"], } diff --git a/src/lang/fr.ts b/src/lang/fr.ts index 7ae74e51..dc68faec 100644 --- a/src/lang/fr.ts +++ b/src/lang/fr.ts @@ -3057,7 +3057,7 @@ - Height (m) + Depth (m) Hauteur (m) From 3693c32d4bdbd2f07cbc400777688c7c03a0d363 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 7 Aug 2024 17:49:52 +0200 Subject: [PATCH 15/29] auto BC generation --- .../BoundaryCondition/BoundaryCondition.py | 12 +++++ src/Model/Geometry/ProfileXYZ.py | 7 ++- src/View/BoundaryCondition/Edit/Table.py | 12 +++++ .../BoundaryCondition/Edit/UndoCommand.py | 26 +++++++++++ src/View/BoundaryCondition/Edit/Window.py | 45 +++++++++++++++++++ src/View/ui/EditBoundaryConditions.ui | 21 +++++++++ 6 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index eddc52b1..265e8b2f 100644 --- a/src/Model/BoundaryCondition/BoundaryCondition.py +++ b/src/Model/BoundaryCondition/BoundaryCondition.py @@ -371,3 +371,15 @@ class BoundaryCondition(SQLSubModel): d = self._data d[index], d[prev] = d[prev], d[index] self._status.modified() + + def reach(self, river): + edges = [] + if self._node != None: + if river != None: + for edge in river.edges(): + if edge.node1.name == self._node.name: + edges.append(edge.reach) + if edge.node2.name == self._node.name: + edges.append(edge.reach) + + return edges diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 0e29c8c3..911ae890 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -436,8 +436,6 @@ class ProfileXYZ(Profile, SQLSubModel): area += poly.area return area - return poly.area - def wet_radius(self, z): p = self.wet_perimeter(z) a = self.wet_area(z) @@ -482,14 +480,15 @@ class ProfileXYZ(Profile, SQLSubModel): if zz[i] < z: line.append([station[i], zz[i]]) - if zz[i] <= z and zz[i+1] > z: + if zz[i] <= z and zz[i+1] >= z: y = np.interp( z, [zz[i], zz[i+1]], [station[i], station[i+1]] ) line.append([y, z]) - lines.append(geometry.LineString(line)) + if len(line) > 2: + lines.append(geometry.LineString(line)) line = [] return lines diff --git a/src/View/BoundaryCondition/Edit/Table.py b/src/View/BoundaryCondition/Edit/Table.py index d4a9f8a6..c5f268a7 100644 --- a/src/View/BoundaryCondition/Edit/Table.py +++ b/src/View/BoundaryCondition/Edit/Table.py @@ -48,6 +48,7 @@ from Model.BoundaryCondition.BoundaryConditionTypes import ( from View.BoundaryCondition.Edit.UndoCommand import ( SetDataCommand, AddCommand, DelCommand, SortCommand, MoveCommand, PasteCommand, + ReplaceDataCommand, ) _translate = QCoreApplication.translate @@ -200,3 +201,14 @@ class TableModel(PamhyrTableModel): def update(self): # self.auto_sort() self.layoutChanged.emit() + + def replace_data(self, data1, data2): + self.layoutAboutToBeChanged.emit() + self._undo.push( + ReplaceDataCommand( + self._data, data1, data2 + ) + ) + self.layoutAboutToBeChanged.emit() + self.update() + diff --git a/src/View/BoundaryCondition/Edit/UndoCommand.py b/src/View/BoundaryCondition/Edit/UndoCommand.py index 02e0a48e..d7ef2671 100644 --- a/src/View/BoundaryCondition/Edit/UndoCommand.py +++ b/src/View/BoundaryCondition/Edit/UndoCommand.py @@ -181,3 +181,29 @@ class PasteCommand(QUndoCommand): def redo(self): for bc in self._bcs: self._data.insert(self._row, bc) + +class ReplaceDataCommand(QUndoCommand): + def __init__(self, data, data1, data2): + QUndoCommand.__init__(self) + self._data = data + self._old_rows = len(data) + self._data1 = data1 + self._data2 = data2 + self._rows = len(data1) + + self._old_bc = [] + for row in range(self._old_rows): + self._old_bc.append((row, self._data.get_i(row))) + self._old_bc.sort() + + def undo(self): + self._data.delete_i(list(range(self._rows))) + for row, el in self._old_bc: + self._data.insert(row, el) + + def redo(self): + self._data.delete_i(list(range(self._old_rows))) + for row in range(self._rows): + self._data.add(row) + self._data._set_i_c_v(row, 0, self._data1[row]) + self._data._set_i_c_v(row, 1, self._data2[row]) diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index 94557ddd..e6b53f23 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -20,6 +20,8 @@ import logging from tools import timer, trace +from numpy import sqrt + from View.Tools.PamhyrWindow import PamhyrWindow from View.Tools.PamhyrWidget import PamhyrWidget from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate @@ -104,6 +106,7 @@ class EditBoundaryConditionWindow(PamhyrWindow): self._data = data trad = BCETranslate() self._long_types = trad.get_dict("long_types") + self._study = study name = trad[self._pamhyr_name] if self._data is not None: @@ -193,6 +196,14 @@ class EditBoundaryConditionWindow(PamhyrWindow): self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_sort").triggered.connect(self.sort) + self.find(QAction, "action_generate_uniform").triggered.connect(self.generate_uniform) + self.find(QAction, "action_generate_critical").triggered.connect(self.generate_critical) + 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) + else: + self.find(QAction, "action_generate_uniform").setVisible(True) + self.find(QAction, "action_generate_critical").setVisible(True) self._table.dataChanged.connect(self.update) self._table.layoutChanged.connect(self.update) @@ -311,3 +322,37 @@ class EditBoundaryConditionWindow(PamhyrWindow): self._table.redo() self.plot.update() self.widget_update() + + def generate_uniform(self): + if self._data.has_node: + node = self._data.node + reach = self._data.reach(self._study.river)[0] + profile = reach.profiles[-1] + incline = reach.get_incline_median_mean() + z_min = profile.z_min() + z_max = profile.z_max() + strickler = 25 + 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))) + for h in height + ] + self._table.replace_data(height, q) + + return + + def generate_critical(self): + if self._data.has_node: + node = self._data.node + reach = self._data.reach(self._study.river)[0] + profile = reach.profiles[-1] + incline = reach.get_incline_median_mean() + z_min = profile.z_min() + z_max = profile.z_max() + height = [(i)*(z_max-z_min)/50 for i in range(51)] + q = [sqrt(9.81 * (profile.wet_area(z_min + h) ** 3) / profile.wet_width(z_min + h)) + for h in height + ] + self._table.replace_data(height, q) + return diff --git a/src/View/ui/EditBoundaryConditions.ui b/src/View/ui/EditBoundaryConditions.ui index b27232d2..c2c81378 100644 --- a/src/View/ui/EditBoundaryConditions.ui +++ b/src/View/ui/EditBoundaryConditions.ui @@ -73,6 +73,8 @@ + + @@ -119,6 +121,25 @@ Sort points + + + Generate uniform + + + Generate uniform + + + Generate rating curve from Manning law + + + + + Generate critical + + + Generate rating curve as Q(z) = Sqrt(g*S(z)^3/L(z)) + + From 845bf3c325e1e5c8055eb0e36391654629d3d641 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 7 Aug 2024 17:56:12 +0200 Subject: [PATCH 16/29] pep8 --- .../BoundaryCondition/BoundaryCondition.py | 4 ++-- src/View/BoundaryCondition/Edit/Table.py | 1 - .../BoundaryCondition/Edit/UndoCommand.py | 1 + src/View/BoundaryCondition/Edit/Window.py | 20 ++++++++++--------- src/View/Results/Window.py | 3 ++- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index 265e8b2f..455365d6 100644 --- a/src/Model/BoundaryCondition/BoundaryCondition.py +++ b/src/Model/BoundaryCondition/BoundaryCondition.py @@ -374,8 +374,8 @@ class BoundaryCondition(SQLSubModel): def reach(self, river): edges = [] - if self._node != None: - if river != None: + if self._node is not None: + if river is not None: for edge in river.edges(): if edge.node1.name == self._node.name: edges.append(edge.reach) diff --git a/src/View/BoundaryCondition/Edit/Table.py b/src/View/BoundaryCondition/Edit/Table.py index c5f268a7..a7481666 100644 --- a/src/View/BoundaryCondition/Edit/Table.py +++ b/src/View/BoundaryCondition/Edit/Table.py @@ -211,4 +211,3 @@ class TableModel(PamhyrTableModel): ) self.layoutAboutToBeChanged.emit() self.update() - diff --git a/src/View/BoundaryCondition/Edit/UndoCommand.py b/src/View/BoundaryCondition/Edit/UndoCommand.py index d7ef2671..4dcc811b 100644 --- a/src/View/BoundaryCondition/Edit/UndoCommand.py +++ b/src/View/BoundaryCondition/Edit/UndoCommand.py @@ -182,6 +182,7 @@ class PasteCommand(QUndoCommand): for bc in self._bcs: self._data.insert(self._row, bc) + class ReplaceDataCommand(QUndoCommand): def __init__(self, data, data1, data2): QUndoCommand.__init__(self) diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index e6b53f23..4b4798a6 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -196,8 +196,12 @@ class EditBoundaryConditionWindow(PamhyrWindow): self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_sort").triggered.connect(self.sort) - self.find(QAction, "action_generate_uniform").triggered.connect(self.generate_uniform) - self.find(QAction, "action_generate_critical").triggered.connect(self.generate_critical) + self.find(QAction, "action_generate_uniform").triggered.connect( + self.generate_uniform + ) + self.find(QAction, "action_generate_critical").triggered.connect( + self.generate_critical + ) 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) @@ -334,10 +338,8 @@ class EditBoundaryConditionWindow(PamhyrWindow): strickler = 25 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))) - for h in height - ] + * (h ** (5/3)) * (abs(incline) ** (0.5))) + for h in height] self._table.replace_data(height, q) return @@ -351,8 +353,8 @@ class EditBoundaryConditionWindow(PamhyrWindow): z_min = profile.z_min() z_max = profile.z_max() height = [(i)*(z_max-z_min)/50 for i in range(51)] - q = [sqrt(9.81 * (profile.wet_area(z_min + h) ** 3) / profile.wet_width(z_min + h)) - for h in height - ] + q = [sqrt(9.81 * (profile.wet_area(z_min + h) ** 3) + / profile.wet_width(z_min + h)) + for h in height] self._table.replace_data(height, q) return diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 726483e0..2a7f463f 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -128,7 +128,8 @@ class ResultsWindow(PamhyrWindow): undo=self._undo_stack, opt_data=t ) - self._table[t]._timestamp = self._timestamps[self._slider_time.value()] + self._table[t]._timestamp = self._timestamps[ + self._slider_time.value()] def setup_slider(self): self._slider_profile = self.find(QSlider, f"verticalSlider_profile") From 4d2ba40287425dbd13a03c7000745d05fb8f2ce2 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Thu, 8 Aug 2024 11:43:42 +0200 Subject: [PATCH 17/29] work on get friction values from rk --- .../BoundaryCondition/BoundaryCondition.py | 8 ++++---- src/Model/Friction/Friction.py | 16 ++++++++++++++++ .../InitialConditions/InitialConditions.py | 18 +++++++++++++++--- src/View/BoundaryCondition/Edit/Window.py | 8 +++++++- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index 455365d6..fd62afe9 100644 --- a/src/Model/BoundaryCondition/BoundaryCondition.py +++ b/src/Model/BoundaryCondition/BoundaryCondition.py @@ -373,13 +373,13 @@ class BoundaryCondition(SQLSubModel): self._status.modified() def reach(self, river): - edges = [] + r = [] if self._node is not None: if river is not None: for edge in river.edges(): if edge.node1.name == self._node.name: - edges.append(edge.reach) + r.append(edge.reach) if edge.node2.name == self._node.name: - edges.append(edge.reach) + r.append(edge.reach) - return edges + return r diff --git a/src/Model/Friction/Friction.py b/src/Model/Friction/Friction.py index d7b0386e..9f0e0f3c 100644 --- a/src/Model/Friction/Friction.py +++ b/src/Model/Friction/Friction.py @@ -22,6 +22,8 @@ from tools import trace, timer from Model.Tools.PamhyrDB import SQLSubModel +from numpy import interp + logger = logging.getLogger() @@ -241,3 +243,17 @@ class Friction(SQLSubModel): def end_strickler(self, strickler): self._end_strickler = strickler self._status.modified() + + def get_friction(self, rk): + if not self.contains_rk(rk): + return None + minor = interp(rk, + [self.begin_rk, self.end_rk], + [self.begin_strickler.minor, + self.end_strickler.minor]) + medium = interp(rk, + [self.begin_rk, self.end_rk], + [self.begin_strickler.medium, + self.end_strickler.medium]) + + return minor, medium diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py index 1b923cda..a4becbae 100644 --- a/src/Model/InitialConditions/InitialConditions.py +++ b/src/Model/InitialConditions/InitialConditions.py @@ -395,8 +395,14 @@ class InitialConditions(SQLSubModel): logger.debug(f"incline = {incline}") self._data = [] for profile in profiles: - width = profile.width_approximation() - strickler = 25 + width = profile.wet_width(profile.z_min() + height) + frictions = self._reach.frictions.frictions + strickler = None + for f in frictions: + if f.contains_rk(profile.rk): + strickler = f.get_friction(profile.rk)[0] + if strickler is None: + strickler = 25.0 if not compute_discharge: discharge = data_discharge[profile.rk] @@ -449,7 +455,13 @@ class InitialConditions(SQLSubModel): self._data = [] for profile in profiles: width = profile.width_approximation() - strickler = 25 + frictions = self._reach.frictions.frictions + strickler = None + for f in frictions: + if f.contains_rk(profile.rk): + strickler = f.get_friction(profile.rk)[0] + if strickler is None: + strickler = 25.0 if not compute_height: height = data_height[profile.rk] diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index 4b4798a6..d639a3a3 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -333,9 +333,15 @@ class EditBoundaryConditionWindow(PamhyrWindow): reach = self._data.reach(self._study.river)[0] profile = reach.profiles[-1] incline = reach.get_incline_median_mean() + frictions = reach._parent.frictions.frictions z_min = profile.z_min() z_max = profile.z_max() - strickler = 25 + strickler = None + for f in frictions: + if f.contains_rk(profile.rk): + strickler = f.get_friction(profile.rk)[0] + if strickler is None: + 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))) From 16916f44b9e83f510ee14af863e161d903f72bba Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Thu, 8 Aug 2024 15:43:06 +0200 Subject: [PATCH 18/29] add slope option for BC generation --- .../BoundaryCondition/Edit/GenerateDialog.py | 68 +++++++++++++ src/View/BoundaryCondition/Edit/Window.py | 10 +- src/View/BoundaryCondition/Edit/translate.py | 2 + .../ui/BoundaryConditionsDialogGenerator.ui | 98 +++++++++++++++++++ 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 src/View/BoundaryCondition/Edit/GenerateDialog.py create mode 100644 src/View/ui/BoundaryConditionsDialogGenerator.ui diff --git a/src/View/BoundaryCondition/Edit/GenerateDialog.py b/src/View/BoundaryCondition/Edit/GenerateDialog.py new file mode 100644 index 00000000..3b07a4d0 --- /dev/null +++ b/src/View/BoundaryCondition/Edit/GenerateDialog.py @@ -0,0 +1,68 @@ +# GenerateDialog.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, QPushButton +) + + +class GenerateDialog(PamhyrDialog): + _pamhyr_ui = "BoundaryConditionsDialogGenerator" + _pamhyr_name = "Boundary Condition Options" + + def __init__(self, + value, + reach, + title="Boundary Condition Options", + trad=None, + parent=None): + super(GenerateDialog, self).__init__( + title=trad[self._pamhyr_name], + options=[], + trad=trad, + parent=parent + ) + + self.value = value + self.find(QDoubleSpinBox, "doubleSpinBox").setValue(self.value) + self.reach = reach + self.find(QPushButton, "EstimateButton").clicked.connect( + self.estimate + ) + + def accept(self): + self.value = self.find(QDoubleSpinBox, "doubleSpinBox").value() + super().accept() + + def reject(self): + self.close() + + def estimate(self): + self.value = abs(self.reach.get_incline_median_mean()) + self.find(QDoubleSpinBox, "doubleSpinBox").setValue(self.value) diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index d639a3a3..168958bd 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -50,6 +50,7 @@ from View.BoundaryCondition.Edit.translate import BCETranslate from View.BoundaryCondition.Edit.UndoCommand import SetMetaDataCommand from View.BoundaryCondition.Edit.Table import TableModel from View.BoundaryCondition.Edit.Plot import Plot +from View.BoundaryCondition.Edit.GenerateDialog import GenerateDialog _translate = QCoreApplication.translate @@ -332,7 +333,13 @@ class EditBoundaryConditionWindow(PamhyrWindow): node = self._data.node reach = self._data.reach(self._study.river)[0] profile = reach.profiles[-1] - incline = reach.get_incline_median_mean() + incline = abs(reach.get_incline_median_mean()) + dlg = GenerateDialog(incline, + reach, + trad=self._trad, + parent=self) + if dlg.exec(): + incline = dlg.value frictions = reach._parent.frictions.frictions z_min = profile.z_min() z_max = profile.z_max() @@ -355,7 +362,6 @@ class EditBoundaryConditionWindow(PamhyrWindow): node = self._data.node reach = self._data.reach(self._study.river)[0] profile = reach.profiles[-1] - incline = reach.get_incline_median_mean() z_min = profile.z_min() z_max = profile.z_max() height = [(i)*(z_max-z_min)/50 for i in range(51)] diff --git a/src/View/BoundaryCondition/Edit/translate.py b/src/View/BoundaryCondition/Edit/translate.py index 9d835550..006c49df 100644 --- a/src/View/BoundaryCondition/Edit/translate.py +++ b/src/View/BoundaryCondition/Edit/translate.py @@ -32,6 +32,8 @@ class BCETranslate(BCTranslate): self._dict["Edit Boundary Conditions"] = _translate( "BoundaryCondition", "Edit boundary conditions" ) + self._dict["Boundary Condition Options"] = _translate( + "BoundaryCondition", "Boundary Condition Options") self._sub_dict["table_headers"] = { "x": _translate("BoundaryCondition", "X"), diff --git a/src/View/ui/BoundaryConditionsDialogGenerator.ui b/src/View/ui/BoundaryConditionsDialogGenerator.ui new file mode 100644 index 00000000..6d3793bf --- /dev/null +++ b/src/View/ui/BoundaryConditionsDialogGenerator.ui @@ -0,0 +1,98 @@ + + + Dialog + + + + 0 + 0 + 356 + 107 + + + + Options + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Slope + + + + + + + 6 + + + 999999.998999999952503 + + + + + + + Estimate + + + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From 0ee47e95da31793a4545034835a5052cfeb40ac7 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Fri, 9 Aug 2024 10:03:55 +0200 Subject: [PATCH 19/29] add froude in results --- src/View/Results/CustomPlot/Plot.py | 45 +++++++++++++++++++++++- src/View/Results/CustomPlot/Translate.py | 3 ++ src/View/Results/Table.py | 11 +++++- src/View/Results/translate.py | 1 + src/View/Translate.py | 1 + 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index f9ebdfcf..cb45863c 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -20,6 +20,7 @@ import logging from functools import reduce from datetime import datetime +from numpy import sqrt from tools import timer from View.Tools.PamhyrPlot import PamhyrPlot @@ -35,6 +36,7 @@ unit = { "velocity": "2-ms", "depth": "3-meter", "mean_depth": "3-meter", + "froude": "4-dimensionless", } @@ -185,7 +187,31 @@ class CustomPlot(PamhyrPlot): rk, d, color='orange', lw=1., ) - lines["depth"] = line + lines["mean_depth"] = line + + if "froude" in self._y: + + ax = self._axes[unit["froude"]] + fr = list( + map( + lambda p: + p.geometry.speed( + p.get_ts_key(self._timestamp, "Q"), + p.get_ts_key(self._timestamp, "Z")) / + sqrt(9.81 * ( + p.geometry.wet_width( + p.get_ts_key(self._timestamp, "Z")) / + p.geometry.wet_area( + p.get_ts_key(self._timestamp, "Z")) + )), + reach.profiles + ) + ) + + line = ax.plot( + rk, fr, color='black', linestyle='--', lw=1., + ) + lines["froude"] = line # Legend lns = reduce( @@ -343,6 +369,23 @@ class CustomPlot(PamhyrPlot): ) lines["depth"] = line + if "froude" in self._y: + + ax = self._axes[unit["froude"]] + d = list( + map(lambda z, q: + profile.geometry.speed(q, z) / + sqrt(9.81 * ( + profile.geometry.wet_width(z) / + profile.geometry.wet_area(z)) + ), z, q) + ) + + line = ax.plot( + ts, d, color='black', linestyle='--', lw=1., + ) + lines["froude"] = line + self._customize_x_axes_time(ts) # Legend diff --git a/src/View/Results/CustomPlot/Translate.py b/src/View/Results/CustomPlot/Translate.py index df4ad75b..6d96bf27 100644 --- a/src/View/Results/CustomPlot/Translate.py +++ b/src/View/Results/CustomPlot/Translate.py @@ -47,6 +47,7 @@ class CustomPlotTranslate(ResultsTranslate): self._dict['wet_area'] = self._dict["unit_wet_area"] self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"] self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"] + self._dict['froude'] = self._dict["unit_froude"] # Unit corresponding long name (plot axes display) @@ -56,6 +57,7 @@ class CustomPlotTranslate(ResultsTranslate): self._dict['1-m3s'] = self._dict["unit_discharge"] self._dict['2-ms'] = self._dict["unit_speed"] self._dict['3-meter'] = self._dict["unit_height"] + self._dict['4-dimensionless'] = self._dict["unit_froude"] # SubDict @@ -70,4 +72,5 @@ class CustomPlotTranslate(ResultsTranslate): "velocity": self._dict["velocity"], "depth": self._dict["max_depth"], "mean_depth": self._dict["mean_depth"], + "froude": self._dict["froude"], } diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py index 1088ac26..fead357e 100644 --- a/src/View/Results/Table.py +++ b/src/View/Results/Table.py @@ -19,6 +19,8 @@ import logging import traceback +from numpy import sqrt + from tools import timer, trace from PyQt5.QtGui import ( @@ -89,7 +91,6 @@ class TableModel(PamhyrTableModel): elif self._headers[column] == "velocity": q = self._lst[row].get_ts_key(self._timestamp, "Q") z = self._lst[row].get_ts_key(self._timestamp, "Z") - v = self._lst[row].geometry.speed(q, z) return f"{v:.4f}" elif self._headers[column] == "width": @@ -116,6 +117,14 @@ class TableModel(PamhyrTableModel): z = self._lst[row].get_ts_key(self._timestamp, "Z") v = self._lst[row].geometry.wet_radius(z) return f"{v:.4f}" + elif self._headers[column] == "froude": + q = self._lst[row].get_ts_key(self._timestamp, "Q") + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.speed(q, z) + b = self._lst[row].geometry.wet_area(z) + a = self._lst[row].geometry.wet_width(z) + froude = v / sqrt(9.81 * (a / b)) + return f"{froude:.4f}" else: v = 0.0 return f"{v:.4f}" diff --git a/src/View/Results/translate.py b/src/View/Results/translate.py index bf96ba13..faab6ecb 100644 --- a/src/View/Results/translate.py +++ b/src/View/Results/translate.py @@ -64,4 +64,5 @@ class ResultsTranslate(MainTranslate): "wet_area": self._dict["unit_wet_area"], "wet_perimeter": self._dict["unit_wet_perimeter"], "hydraulic_radius": self._dict["unit_hydraulic_radius"], + "froude": self._dict["unit_froude"], } diff --git a/src/View/Translate.py b/src/View/Translate.py index 1a47f238..30a8a564 100644 --- a/src/View/Translate.py +++ b/src/View/Translate.py @@ -80,6 +80,7 @@ class UnitTranslate(CommonWordTranslate): self._dict["unit_hydraulic_radius"] = _translate( "Unit", "Hydraulic Radius (m)" ) + self._dict["unit_froude"] = _translate("Unit", "Froude number") class MainTranslate(UnitTranslate): From 3c9bc6ce73deef17593444ec80b0805a1c3389e9 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Fri, 9 Aug 2024 11:25:24 +0200 Subject: [PATCH 20/29] add froude in results --- src/View/Results/CustomPlot/Plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index cb45863c..b699f3ea 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -439,7 +439,7 @@ class CustomPlot(PamhyrPlot): @timer def update(self): - if not self._init: + #if not self._init: self.draw() return From 79660bfd6eb3b717f6f695c1fc613b07bd3a43ae Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Fri, 9 Aug 2024 11:59:50 +0200 Subject: [PATCH 21/29] debug --- src/View/Results/CustomPlot/Plot.py | 2 +- src/View/Results/Window.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index b699f3ea..cb45863c 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -439,7 +439,7 @@ class CustomPlot(PamhyrPlot): @timer def update(self): - #if not self._init: + if not self._init: self.draw() return diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 2a7f463f..9068876b 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -538,6 +538,9 @@ class ResultsWindow(PamhyrWindow): ) plot.draw() + # Add plot to additional plot + self._additional_plot[name] = plot + grid.addWidget(toolbar, 0, 0) grid.addWidget(canvas, 1, 0) widget.setLayout(grid) @@ -669,4 +672,5 @@ class ResultsWindow(PamhyrWindow): def delete_tab(self, index): tab_widget = self.find(QTabWidget, f"tabWidget") + self._additional_plot.pop(tab_widget.tabText(index)) tab_widget.removeTab(index) From 5d3111df379a2b7a23f85ee0edc70a3d598f1139 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Fri, 9 Aug 2024 17:27:16 +0200 Subject: [PATCH 22/29] debug --- src/Solver/Mage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py index 37701e65..aa1bc521 100644 --- a/src/Solver/Mage.py +++ b/src/Solver/Mage.py @@ -264,7 +264,7 @@ class Mage(CommandLineSolver): if t in ["HYD", "QSO", "LIM"]: v0 /= 60 # Convert first column to minute - f.write(f"{v0:10}{v1:10}\n") + f.write(f"{v0:9} {v1:9} \n") return files From ec5b9571d3ccf2ec85be13f24b08a3d8ef3aace3 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 21 Aug 2024 14:21:02 +0200 Subject: [PATCH 23/29] debug --- src/Model/Geometry/ProfileXYZ.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 911ae890..896388ef 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -491,6 +491,12 @@ class ProfileXYZ(Profile, SQLSubModel): lines.append(geometry.LineString(line)) line = [] + if zz[self.number_points-1] < z: + line.append([station[self.number_points-1], z]) + if len(line) > 2: + lines.append(geometry.LineString(line)) + line = [] + return lines def max_water_depth(self, z): From 898aad3da5f3c76ae5652e25c2ff38ac2dc193da Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Wed, 21 Aug 2024 15:45:42 +0200 Subject: [PATCH 24/29] add wet arrea in custom plots + slider mark --- doc/users/Tuto1/step2.pamhyr | Bin 200704 -> 208896 bytes doc/users/Tuto1/step3.pamhyr | Bin 229376 -> 237568 bytes src/View/Results/CustomPlot/Plot.py | 64 ++++++++++++++++++++++- src/View/Results/CustomPlot/Translate.py | 2 + src/View/Results/PlotH.py | 31 +++++------ 5 files changed, 78 insertions(+), 19 deletions(-) diff --git a/doc/users/Tuto1/step2.pamhyr b/doc/users/Tuto1/step2.pamhyr index e10e685b6ce4b9e25e4542a1b28f15fdaa745870..c7516dff606939dd005eafa8e54776cee528a4ee 100644 GIT binary patch delta 575 zcmZozz|-)6XM(gKI|BoQF%ZLm+(aE?M)r*f^Z6xtc=@dvIC!lX_`Ue`c&+#k@o(dw z%%9I6v$0W$ce9l&3zH!~OdF6EU}Tn-EK4ma&dkp<&@<38G!*1uR_8A+DNU)2FG|fx zO)O3|n4B-4#i%v0QB8sisD*>Shk<`1|6%?+{PXzV@b~ba;s3g^v4mepkXfA(MI*=N zdHQJ^n0Xi@HnTqnWZazoD~FM*nS+~M+|-n@)p&B@FUiTzAAMjd%AV}_UWP3-FC`wr zoLv8(XS4E?Qf4et>wfZ1p7Bb2^PlI2EKupq-fy-s%X6hO@G#ghaI9rN%C?5}0rMYb z3C1IQl04fO?6_BPZJ6jNx7qGPHz!CB<5$_qFMigu0Da>*vC-A6v0Q>(Twb2B*|sDx zDJQikwIDtxGcUCm&M6$t(c|bYf0?T4qjaF*@G}#THaq6w3+{OEN%~ tF_)xPOrH2nX7lzxPE63im>m0`gAp8{%{KqrZT>TExB1U>L}1Yd1puoH#})tp delta 325 zcmZp8z|*jRXM(gKD+2?AArQlW$V44uM%Il9^ZCWOdHJmw*mj^N~lwj7-@D zlO5m7u%+gu#6y^q>;Lm?R(?{-j74hQPu|HhUWsr1^W2aHD!tkJ%{FFv?#m3^40a4$ z=^Sg>kFu>{eZc&OS%UEhpCr#V20O+*6CLF?+kNQf1nFV?Dm(eb&-%>~4|5nFUvH5B diff --git a/doc/users/Tuto1/step3.pamhyr b/doc/users/Tuto1/step3.pamhyr index 00a39e3a44170e11157d417d529b9238a17d726a..018a4aea279cf4566b96ce30dc3229f0bf7133cf 100644 GIT binary patch delta 580 zcmZo@;A=R*H$hrZl7WH23W#AqZlaDcqvXbf`TUYRy!_S-9K2Qx{NDU}yjJ{&__y&- z<}cun-K?mf#Jkx_mW9bk0HzN_3otTEOO~Y;6=&w>8R!}485#<5Fst(ymz1Vd#uugL zq$U=p8cZ&b&t%k^tSF!+!3EUA!QaEczmfki{~i8${BQVs_|Ncv1)5*NFC@sU&WNIw zWAi-yv<=KWj1imJ9|SUPPXCp|$koij%`R?g%Ghc=x$u|d=-!KvL9tz!}@^v z53>a05k5(tZ47qYtGG5ybd=j{_o15;q=)gV?Bo|e>sf%_@dSFz)vU2xf?Zr*p0U}s zBrz!`wJ5b9J|{CTwHVH@at?BJ3~^Nmadh%=Re%alF8F6V`RqTn$$Q@_uz=K0R(vYM zUYwr;3ew4|ze{;Haf-8x>*_K#yMoP4Oi9Ts0fu#APJCKsPHHha-x9?ZR9O_u3KB~) vK$bC=q*hFx_)TW>_CHQc(9oD1`=5gm9HPxO|J!Z;Gj6x}&vc|=(FO$oq_oCx delta 328 zcmZoTz}L{gH$hrZoPmMC0*GNiWTK8Sqxi;z`TXMCy!_S-?7UVC{O|eoc&+#k@o(dw z%wMosQ9*@wvz06h({#fIMx)6E@|ld16CFj>`5Bp|#miEQiZk=`4D<~2%(;Muvh&Yl z;NQr9nEwv{JpMQQXZXKvRy63~-#kx0Z38nm(~-^W4+0rk_ Date: Wed, 21 Aug 2024 15:48:11 +0200 Subject: [PATCH 25/29] pep8 --- doc/users/Tuto1/step2.pamhyr | Bin 208896 -> 200704 bytes doc/users/Tuto1/step3.pamhyr | Bin 237568 -> 229376 bytes src/Model/Geometry/ProfileXYZ.py | 8 ++++---- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/users/Tuto1/step2.pamhyr b/doc/users/Tuto1/step2.pamhyr index c7516dff606939dd005eafa8e54776cee528a4ee..e10e685b6ce4b9e25e4542a1b28f15fdaa745870 100644 GIT binary patch delta 325 zcmZp8z|*jRXM(gKD+2?AArQlW$V44uM%Il9^ZCWOdHJmw*mj^N~lwj7-@D zlO5m7u%+gu#6y^q>;Lm?R(?{-j74hQPu|HhUWsr1^W2aHD!tkJ%{FFv?#m3^40a4$ z=^Sg>kFu>{eZc&OS%UEhpCr#V20O+*6CLF?+kNQf1nFV?Dm(eb&-%>~4|5nFUvH5B delta 575 zcmZozz|-)6XM(gKI|BoQF%ZLm+(aE?M)r*f^Z6xtc=@dvIC!lX_`Ue`c&+#k@o(dw z%%9I6v$0W$ce9l&3zH!~OdF6EU}Tn-EK4ma&dkp<&@<38G!*1uR_8A+DNU)2FG|fx zO)O3|n4B-4#i%v0QB8sisD*>Shk<`1|6%?+{PXzV@b~ba;s3g^v4mepkXfA(MI*=N zdHQJ^n0Xi@HnTqnWZazoD~FM*nS+~M+|-n@)p&B@FUiTzAAMjd%AV}_UWP3-FC`wr zoLv8(XS4E?Qf4et>wfZ1p7Bb2^PlI2EKupq-fy-s%X6hO@G#ghaI9rN%C?5}0rMYb z3C1IQl04fO?6_BPZJ6jNx7qGPHz!CB<5$_qFMigu0Da>*vC-A6v0Q>(Twb2B*|sDx zDJQikwIDtxGcUCm&M6$t(c|bYf0?T4qjaF*@G}#THaq6w3+{OEN%~ tF_)xPOrH2nX7lzxPE63im>m0`gAp8{%{KqrZT>TExB1U>L}1Yd1puoH#})tp diff --git a/doc/users/Tuto1/step3.pamhyr b/doc/users/Tuto1/step3.pamhyr index 018a4aea279cf4566b96ce30dc3229f0bf7133cf..00a39e3a44170e11157d417d529b9238a17d726a 100644 GIT binary patch delta 328 zcmZoTz}L{gH$hrZoPmMC0*GNiWTK8Sqxi;z`TXMCy!_S-?7UVC{O|eoc&+#k@o(dw z%wMosQ9*@wvz06h({#fIMx)6E@|ld16CFj>`5Bp|#miEQiZk=`4D<~2%(;Muvh&Yl z;NQr9nEwv{JpMQQXZXKvRy63~-#kx0Z38nm(~-^W4+0rk_8R!}485#<5Fst(ymz1Vd#uugL zq$U=p8cZ&b&t%k^tSF!+!3EUA!QaEczmfki{~i8${BQVs_|Ncv1)5*NFC@sU&WNIw zWAi-yv<=KWj1imJ9|SUPPXCp|$koij%`R?g%Ghc=x$u|d=-!KvL9tz!}@^v z53>a05k5(tZ47qYtGG5ybd=j{_o15;q=)gV?Bo|e>sf%_@dSFz)vU2xf?Zr*p0U}s zBrz!`wJ5b9J|{CTwHVH@at?BJ3~^Nmadh%=Re%alF8F6V`RqTn$$Q@_uz=K0R(vYM zUYwr;3ew4|ze{;Haf-8x>*_K#yMoP4Oi9Ts0fu#APJCKsPHHha-x9?ZR9O_u3KB~) vK$bC=q*hFx_)TW>_CHQc(9oD1`=5gm9HPxO|J!Z;Gj6x}&vc|=(FO$oq_oCx diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 896388ef..14187233 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -492,10 +492,10 @@ class ProfileXYZ(Profile, SQLSubModel): line = [] if zz[self.number_points-1] < z: - line.append([station[self.number_points-1], z]) - if len(line) > 2: - lines.append(geometry.LineString(line)) - line = [] + line.append([station[self.number_points-1], z]) + if len(line) > 2: + lines.append(geometry.LineString(line)) + line = [] return lines From 5204b2cef241768d5786a35b26680c5380d07caf Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Tue, 27 Aug 2024 17:03:56 +0200 Subject: [PATCH 26/29] debug Froude --- doc/users/Tuto1/step2.pamhyr | Bin 200704 -> 208896 bytes src/View/Results/CustomPlot/Plot.py | 8 ++++---- src/View/Results/Table.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/users/Tuto1/step2.pamhyr b/doc/users/Tuto1/step2.pamhyr index e10e685b6ce4b9e25e4542a1b28f15fdaa745870..c7516dff606939dd005eafa8e54776cee528a4ee 100644 GIT binary patch delta 575 zcmZozz|-)6XM(gKI|BoQF%ZLm+(aE?M)r*f^Z6xtc=@dvIC!lX_`Ue`c&+#k@o(dw z%%9I6v$0W$ce9l&3zH!~OdF6EU}Tn-EK4ma&dkp<&@<38G!*1uR_8A+DNU)2FG|fx zO)O3|n4B-4#i%v0QB8sisD*>Shk<`1|6%?+{PXzV@b~ba;s3g^v4mepkXfA(MI*=N zdHQJ^n0Xi@HnTqnWZazoD~FM*nS+~M+|-n@)p&B@FUiTzAAMjd%AV}_UWP3-FC`wr zoLv8(XS4E?Qf4et>wfZ1p7Bb2^PlI2EKupq-fy-s%X6hO@G#ghaI9rN%C?5}0rMYb z3C1IQl04fO?6_BPZJ6jNx7qGPHz!CB<5$_qFMigu0Da>*vC-A6v0Q>(Twb2B*|sDx zDJQikwIDtxGcUCm&M6$t(c|bYf0?T4qjaF*@G}#THaq6w3+{OEN%~ tF_)xPOrH2nX7lzxPE63im>m0`gAp8{%{KqrZT>TExB1U>L}1Yd1puoH#})tp delta 325 zcmZp8z|*jRXM(gKD+2?AArQlW$V44uM%Il9^ZCWOdHJmw*mj^N~lwj7-@D zlO5m7u%+gu#6y^q>;Lm?R(?{-j74hQPu|HhUWsr1^W2aHD!tkJ%{FFv?#m3^40a4$ z=^Sg>kFu>{eZc&OS%UEhpCr#V20O+*6CLF?+kNQf1nFV?Dm(eb&-%>~4|5nFUvH5B diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index 0f2ed5cb..a3073c7a 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -200,9 +200,9 @@ class CustomPlot(PamhyrPlot): p.get_ts_key(self._timestamp, "Q"), p.get_ts_key(self._timestamp, "Z")) / sqrt(9.81 * ( - p.geometry.wet_width( - p.get_ts_key(self._timestamp, "Z")) / p.geometry.wet_area( + p.get_ts_key(self._timestamp, "Z")) / + p.geometry.wet_width( p.get_ts_key(self._timestamp, "Z")) )), reach.profiles @@ -394,8 +394,8 @@ class CustomPlot(PamhyrPlot): map(lambda z, q: profile.geometry.speed(q, z) / sqrt(9.81 * ( - profile.geometry.wet_width(z) / - profile.geometry.wet_area(z)) + profile.geometry.wet_area(z) / + profile.geometry.wet_width(z)) ), z, q) ) diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py index fead357e..c92e994e 100644 --- a/src/View/Results/Table.py +++ b/src/View/Results/Table.py @@ -121,8 +121,8 @@ class TableModel(PamhyrTableModel): q = self._lst[row].get_ts_key(self._timestamp, "Q") z = self._lst[row].get_ts_key(self._timestamp, "Z") v = self._lst[row].geometry.speed(q, z) - b = self._lst[row].geometry.wet_area(z) - a = self._lst[row].geometry.wet_width(z) + a = self._lst[row].geometry.wet_area(z) + b = self._lst[row].geometry.wet_width(z) froude = v / sqrt(9.81 * (a / b)) return f"{froude:.4f}" else: From 06e621399bb620ba89bea72f72fc31002f9389bd Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Mon, 2 Sep 2024 11:58:11 +0200 Subject: [PATCH 27/29] debug --- src/Meshing/Mage.py | 2 +- src/Model/Geometry/ProfileXYZ.py | 4 ++-- src/View/Geometry/Profile/Plot.py | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Meshing/Mage.py b/src/Meshing/Mage.py index f01e5b51..ce408ac0 100644 --- a/src/Meshing/Mage.py +++ b/src/Meshing/Mage.py @@ -411,7 +411,7 @@ class MeshingWithMageMailleurTT(AMeshingTool): str, [ st_file, m_file, - "update_rk", step, + "update_kp", step, limites[0], limites[1], directrices[0], directrices[1], orientation, lm, linear, origin, origin_value diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 14187233..7936342a 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -651,8 +651,8 @@ class ProfileXYZ(Profile, SQLSubModel): Returns: Projection of the points of the profile on a plane. """ - if self.nb_points < 3: - return None + if self.nb_points < 2: + return [0.0] else: return self._get_station(self.points) diff --git a/src/View/Geometry/Profile/Plot.py b/src/View/Geometry/Profile/Plot.py index 136fcaf7..0eb1543f 100644 --- a/src/View/Geometry/Profile/Plot.py +++ b/src/View/Geometry/Profile/Plot.py @@ -313,10 +313,6 @@ class Plot(PamhyrPlot): x_carto = self.data.x() y_carto = self.data.y() - if (len(x_carto) < 3 or len(y_carto) < 3 or len(x) < 3): - # Noting to do in this case - return - self.profile_line2D, = self.canvas.axes.plot( x, y, color=self.color_plot, lw=1.5, markersize=7, marker='+', From 5eb030046c27e88b0b15799025e232bb6e71d2e0 Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Thu, 5 Sep 2024 09:27:42 +0200 Subject: [PATCH 28/29] debug default path in geometry --- src/View/Geometry/Window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Geometry/Window.py b/src/View/Geometry/Window.py index 1a621808..6c24b22e 100644 --- a/src/View/Geometry/Window.py +++ b/src/View/Geometry/Window.py @@ -623,7 +623,7 @@ class GeometryWindow(PamhyrWindow): QSettings.UserScope, 'MyOrg' ) - if self._study.filename != "" or self._study.filename is not None: + if self._study.filename != "" and self._study.filename is not None: default_directory = os.path.basename(self._study.filename) current_dir = settings.value( 'current_directory', From 9a7c264ee67e6d1f74c80c1512d9d3bf671ce10d Mon Sep 17 00:00:00 2001 From: Theophile Terraz Date: Thu, 5 Sep 2024 12:06:50 +0200 Subject: [PATCH 29/29] remove results section slider --- src/View/Results/Window.py | 11 ----------- src/View/ui/Results.ui | 13 ------------- 2 files changed, 24 deletions(-) diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 9068876b..03be2da7 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -132,10 +132,7 @@ class ResultsWindow(PamhyrWindow): self._slider_time.value()] def setup_slider(self): - self._slider_profile = self.find(QSlider, f"verticalSlider_profile") default_reach = self._results.river.reach(0) - self._slider_profile.setMaximum(len(default_reach.profiles) - 1) - self._slider_profile.setValue(0) self._slider_time = self.find(QSlider, f"horizontalSlider_time") self._slider_time.setMaximum(len(self._timestamps) - 1) @@ -336,8 +333,6 @@ class ResultsWindow(PamhyrWindow): self._table[t].dataChanged.connect(fun[t]) - self._slider_profile.valueChanged.connect( - self._set_current_profile_slider) self._slider_time.valueChanged.connect(self._set_current_timestamp) self._button_play.setChecked(False) self._button_play.clicked.connect(self._pause) @@ -451,7 +446,6 @@ class ResultsWindow(PamhyrWindow): ind = indexes[0].row() self.update(profile_id=ind) - self._slider_profile.setValue(ind) def _set_current_profile_raw_data(self): table = self.find(QTableView, f"tableView_raw_data") @@ -461,11 +455,6 @@ class ResultsWindow(PamhyrWindow): ind = indexes[0].row() self.update(profile_id=ind) - self._slider_profile.setValue(ind) - - def _set_current_profile_slider(self): - pid = self._slider_profile.value() - self.update(profile_id=pid) def _set_current_timestamp(self): timestamp = self._timestamps[self._slider_time.value()] diff --git a/src/View/ui/Results.ui b/src/View/ui/Results.ui index 479f5f05..256ab368 100644 --- a/src/View/ui/Results.ui +++ b/src/View/ui/Results.ui @@ -39,19 +39,6 @@ - - - - Qt::Vertical - - - true - - - true - - -