From 3693c32d4bdbd2f07cbc400777688c7c03a0d363 Mon Sep 17 00:00:00 2001
From: Theophile Terraz <theophile.terraz@inrae.fr>
Date: Wed, 7 Aug 2024 17:49:52 +0200
Subject: [PATCH] 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 @@
    <addaction name="action_add"/>
    <addaction name="action_del"/>
    <addaction name="action_sort"/>
+   <addaction name="action_generate_uniform"/>
+   <addaction name="action_generate_critical"/>
   </widget>
   <action name="action_add">
    <property name="checkable">
@@ -119,6 +121,25 @@
     <string>Sort points</string>
    </property>
   </action>
+  <action name="action_generate_uniform">
+   <property name="text">
+    <string>Generate uniform</string>
+   </property>
+   <property name="iconText">
+    <string>Generate uniform</string>
+   </property>
+   <property name="toolTip">
+    <string>Generate rating curve from Manning law</string>
+   </property>
+  </action>
+  <action name="action_generate_critical">
+   <property name="text">
+    <string>Generate critical</string>
+   </property>
+   <property name="toolTip">
+    <string>Generate rating curve as Q(z) = Sqrt(g*S(z)^3/L(z))</string>
+   </property>
+  </action>
  </widget>
  <resources/>
  <connections/>
-- 
GitLab