Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eae1f83a73 | ||
|
|
a738bf3679 | ||
|
|
d81f341303 | ||
|
|
1ea6fd7c54 | ||
|
|
3025c995b6 | ||
|
|
ecc75c45eb | ||
|
|
09dcf486d8 | ||
|
|
592c1e5dbc | ||
|
|
cd3baf2870 | ||
|
|
93734ff1b9 | ||
|
|
dfb7d37cfb | ||
|
|
83aa6a5791 | ||
|
|
45f020edaa | ||
|
|
ad1c5c487e | ||
|
|
5c323b28e2 | ||
|
|
da2250df3a | ||
|
|
cb1d6b4db7 | ||
|
|
f96cee145c | ||
|
|
e4670e4a4d | ||
|
|
5c0f90b43e | ||
|
|
e6de528b7f | ||
|
|
dfff8d2fcb | ||
|
|
33a1fd2b52 | ||
|
|
f083b7f6ef | ||
|
|
25de73812b | ||
|
|
6b232d2971 | ||
|
|
9c0ffa8356 | ||
|
|
ffbd2e2413 | ||
|
|
58300b8c1a | ||
|
|
cd2fda8224 | ||
|
|
73ae571de1 | ||
|
|
7aee4c64a3 | ||
|
|
c3cae35de4 | ||
|
|
2736d2f399 | ||
|
|
00afc6c06b | ||
|
|
4eefa88490 | ||
|
|
92e54bf11e | ||
|
|
c11350d49a | ||
|
|
8f5fb4cc3b | ||
|
|
fc7a7bb7fd | ||
|
|
5a1c363e8a | ||
|
|
77a6da3024 | ||
|
|
c4a66281f6 | ||
|
|
7ffc2482fc | ||
|
|
f37da68a67 | ||
|
|
f64ed131f9 | ||
|
|
79b31e8d08 | ||
|
|
d1c8e3e4f5 | ||
|
|
c253ad832d | ||
|
|
6081b09267 | ||
|
|
592c2c9a72 | ||
|
|
ce45776a63 | ||
|
|
02bbdf9fda | ||
|
|
6c9e90ceaf | ||
|
|
b99be5a280 | ||
|
|
1acd582de9 | ||
|
|
8ce5a4d215 | ||
|
|
ccb55ff4e1 | ||
|
|
3ef4b40558 | ||
|
|
c667d94e84 | ||
|
|
1b601460e6 | ||
|
|
7db65f127e | ||
|
|
f302e18d71 | ||
|
|
352c0beb27 | ||
|
|
d6561ab44d | ||
|
|
5d9b6f99c6 | ||
|
|
163a842384 | ||
|
|
eea355a784 | ||
|
|
1ab1cb37b0 | ||
|
|
be19a11387 | ||
|
|
062743d748 | ||
|
|
ac2a2002b7 | ||
|
|
6951b82339 | ||
|
|
e41c23120a | ||
|
|
4c5281ea30 | ||
|
|
7b7ba2eb37 | ||
|
|
4a43653d7f | ||
|
|
0307927fa9 | ||
|
|
db11c9d968 | ||
|
|
d85e5c4666 | ||
|
|
45ee8a9662 | ||
|
|
5ab06dbe51 | ||
|
|
d73e901a96 | ||
|
|
c17e39e3cf | ||
|
|
af4cce96ef | ||
|
|
0a780e5f78 | ||
|
|
fb2b1f680f | ||
|
|
6957986632 | ||
|
|
13d23502a9 | ||
|
|
5fa888768e | ||
|
|
07451899c4 | ||
|
|
2aa693e4ae | ||
|
|
ff480fb593 | ||
|
|
d6753dc986 | ||
|
|
2d2484d8fa | ||
|
|
bb97f1977b | ||
|
|
eb26eba131 | ||
|
|
2d3e214514 | ||
|
|
894a7c8477 | ||
|
|
21197ead23 | ||
|
|
d79a0cc973 | ||
|
|
a55669d181 | ||
|
|
2fd44fbfb4 | ||
|
|
962bfa945d | ||
|
|
65c1163a55 | ||
|
|
8d67442ca3 | ||
|
|
f7d2ac8b86 | ||
|
|
888002cd10 | ||
|
|
e1880c6549 | ||
|
|
9f0caaaa69 | ||
|
|
fa35d49a42 | ||
|
|
4935069864 | ||
|
|
6438d23304 |
No files matched your search
@@ -33,7 +33,7 @@ dl-mage-linux:
|
|||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||||
script:
|
script:
|
||||||
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test6/downloads/packages/mage_linux.tgz
|
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/v8.3.0/downloads/packages/mage_linux.tgz
|
||||||
- mkdir -p mage-linux
|
- mkdir -p mage-linux
|
||||||
- cd mage-linux
|
- cd mage-linux
|
||||||
- tar xvf ../mage.tgz
|
- tar xvf ../mage.tgz
|
||||||
@@ -50,7 +50,7 @@ dl-mage-windows:
|
|||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||||
script:
|
script:
|
||||||
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test6/downloads/packages/mage_windows.tgz
|
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/v8.3.0/downloads/packages/mage_windows.tgz
|
||||||
- mkdir -p mage-windows
|
- mkdir -p mage-windows
|
||||||
- cd mage-windows
|
- cd mage-windows
|
||||||
- tar xvf ../mage.tgz
|
- tar xvf ../mage.tgz
|
||||||
@@ -122,7 +122,7 @@ test-pep8:
|
|||||||
- pip3 install -r ./requirements.txt
|
- pip3 install -r ./requirements.txt
|
||||||
- pip3 install -U -r ./requirements.txt
|
- pip3 install -U -r ./requirements.txt
|
||||||
- pip3 install pycodestyle
|
- pip3 install pycodestyle
|
||||||
- pycodestyle ./src
|
- pycodestyle --exclude="*_to_*.py" ./src
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
#########
|
#########
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
@@ -65,7 +65,8 @@
|
|||||||
31874.0000 14.5000 22.1900
|
31874.0000 14.5000 22.1900
|
||||||
31874.0000 17.6000 22.9900
|
31874.0000 17.6000 22.9900
|
||||||
999.9990 999.9990 999.9990
|
999.9990 999.9990 999.9990
|
||||||
7 0 0 7 31875.0000 PontRD101m
|
7 0 0 9 31875.0000 PontRD101m
|
||||||
|
31875.0000 0.5400 23.0000
|
||||||
31875.0000 0.5400 21.8900
|
31875.0000 0.5400 21.8900
|
||||||
31875.0000 2.5000 19.6900 rg
|
31875.0000 2.5000 19.6900 rg
|
||||||
31875.0000 2.5000 18.6900
|
31875.0000 2.5000 18.6900
|
||||||
@@ -73,8 +74,10 @@
|
|||||||
31875.0000 10.7000 18.9900
|
31875.0000 10.7000 18.9900
|
||||||
31875.0000 10.7000 19.9900 rd
|
31875.0000 10.7000 19.9900 rd
|
||||||
31875.0000 13.8900 21.8900
|
31875.0000 13.8900 21.8900
|
||||||
|
31875.0000 13.8900 23.0000
|
||||||
999.9990 999.9990 999.9990
|
999.9990 999.9990 999.9990
|
||||||
8 0 0 7 31885.0000 PontRD101v
|
8 0 0 9 31885.0000 PontRD101v
|
||||||
|
31885.0000 0.5400 23.0000
|
||||||
31885.0000 0.5400 21.8900
|
31885.0000 0.5400 21.8900
|
||||||
31885.0000 2.5000 19.6900 rg
|
31885.0000 2.5000 19.6900 rg
|
||||||
31885.0000 2.5000 18.6900
|
31885.0000 2.5000 18.6900
|
||||||
@@ -82,6 +85,7 @@
|
|||||||
31885.0000 10.7000 18.9900
|
31885.0000 10.7000 18.9900
|
||||||
31885.0000 10.7000 19.9900 rd
|
31885.0000 10.7000 19.9900 rd
|
||||||
31885.0000 13.8900 21.8900
|
31885.0000 13.8900 21.8900
|
||||||
|
31885.0000 13.8900 23.0000
|
||||||
999.9990 999.9990 999.9990
|
999.9990 999.9990 999.9990
|
||||||
9 0 0 9 31886.0000
|
9 0 0 9 31886.0000
|
||||||
31886.0000 0.0000 22.9900
|
31886.0000 0.0000 22.9900
|
||||||
@@ -303,7 +307,8 @@
|
|||||||
34334.0000 14.0000 18.1000 rd
|
34334.0000 14.0000 18.1000 rd
|
||||||
34334.0000 16.1000 21.1300
|
34334.0000 16.1000 21.1300
|
||||||
999.9990 999.9990 999.9990
|
999.9990 999.9990 999.9990
|
||||||
26 0 0 7 34335.0000 PontmThivencelle
|
26 0 0 9 34335.0000 PontmThivencelle
|
||||||
|
34335.0000 2.9000 22.0000
|
||||||
34335.0000 2.9000 20.7400
|
34335.0000 2.9000 20.7400
|
||||||
34335.0000 2.9000 18.2900 rg
|
34335.0000 2.9000 18.2900 rg
|
||||||
34335.0000 6.4000 17.7900
|
34335.0000 6.4000 17.7900
|
||||||
@@ -311,8 +316,10 @@
|
|||||||
34335.0000 12.8000 17.2600
|
34335.0000 12.8000 17.2600
|
||||||
34335.0000 12.8000 18.3100 rd
|
34335.0000 12.8000 18.3100 rd
|
||||||
34335.0000 12.8000 20.7400
|
34335.0000 12.8000 20.7400
|
||||||
|
34335.0000 12.8000 22.0000
|
||||||
999.9990 999.9990 999.9990
|
999.9990 999.9990 999.9990
|
||||||
27 0 0 7 34343.0000 PontvThivencelle
|
27 0 0 9 34343.0000 PontvThivencelle
|
||||||
|
34343.0000 2.9000 22.0000
|
||||||
34343.0000 2.9000 20.7400
|
34343.0000 2.9000 20.7400
|
||||||
34343.0000 2.9000 18.2900 rg
|
34343.0000 2.9000 18.2900 rg
|
||||||
34343.0000 6.4000 17.7900
|
34343.0000 6.4000 17.7900
|
||||||
@@ -320,6 +327,7 @@
|
|||||||
34343.0000 12.8000 17.2600
|
34343.0000 12.8000 17.2600
|
||||||
34343.0000 12.8000 18.2900 rd
|
34343.0000 12.8000 18.2900 rd
|
||||||
34343.0000 12.8000 20.7400
|
34343.0000 12.8000 20.7400
|
||||||
|
34343.0000 12.8000 22.0000
|
||||||
999.9990 999.9990 999.9990
|
999.9990 999.9990 999.9990
|
||||||
28 0 0 10 34345.0000 avalPontThivencell
|
28 0 0 10 34345.0000 avalPontThivencell
|
||||||
34345.0000 0.0000 21.3600
|
34345.0000 0.0000 21.3600
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ select the cross section named \textit{PontRD101m} and open the edition window.
|
|||||||
|
|
||||||
On the left panel is the list of all the points of the section, with their coordinates, their name and their transversal absisa. The Z coordinate of the highest point is written in blue and the lowest in red. Points can have a name. If a point with the same name exists in every section in a reach, it forms a longitudinal line. For example, here we have \textit{rg} and \textit{rd} which represent the left bank and the right bank of the main chanel.
|
On the left panel is the list of all the points of the section, with their coordinates, their name and their transversal absisa. The Z coordinate of the highest point is written in blue and the lowest in red. Points can have a name. If a point with the same name exists in every section in a reach, it forms a longitudinal line. For example, here we have \textit{rg} and \textit{rd} which represent the left bank and the right bank of the main chanel.
|
||||||
|
|
||||||
On the plot is a projection of the cross section. You can use \texttt{[ctrl + click]} to select a point in the plot. For now we don't have to change anything. You can close the cross section edition window and the geometry edition window.
|
On the plot is a projection of the cross section. You can use \texttt{[ctrl + click]} to select a point in the plot and \texttt{[shift + click]} to select a water line and fisualize usefull geometric data. You can close the cross section edition window and the geometry edition window.
|
||||||
|
|
||||||
\section{Edit the boundary conditions}
|
\section{Edit the boundary conditions}
|
||||||
|
|
||||||
@@ -172,7 +172,8 @@ You can close the the \textit{Edit Boundary Conditions} and the \textit{Boundary
|
|||||||
\section{Create initial conditions}
|
\section{Create initial conditions}
|
||||||
|
|
||||||
From the main window, click on \texttt{[Hydraulics] => [Initial conditions]}.
|
From the main window, click on \texttt{[Hydraulics] => [Initial conditions]}.
|
||||||
Click on \texttt{Generate minimal height} and enter a height of 2 meters in the pop-up window to generate an initial water height condition that verify the folowing conditions: a water height of at least 2 meters and a decreasing water surface elevation from upstream to downstream. You should see:
|
If you don't know the initial conditions in water elevation and flow discharge of the river, you can use \texttt{[Generate minimal height]} or \texttt{[Generate constant discharge]} buttons to let Pamhyr2 estimate an initial condition using the Manning-Strickler formula.
|
||||||
|
Click on \texttt{[Generate minimal height]} and enter a discharge of $4 m^2$ in the pop-up window to generate an initial water height condition based on the Manning-Strickler formula and a uniform discharge of $4 m^2$. You should see:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\includegraphics[width=15cm]{ic.png}
|
\includegraphics[width=15cm]{ic.png}
|
||||||
@@ -184,18 +185,83 @@ Close the \textit{Initial conditions} window.
|
|||||||
\section{Edit friction coefficients}
|
\section{Edit friction coefficients}
|
||||||
|
|
||||||
From the main window, click on \texttt{[Hydraulics] => [Edit friction]}.
|
From the main window, click on \texttt{[Hydraulics] => [Edit friction]}.
|
||||||
You fist have to define sets of Strickler coefficients. Click on \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"} to open the \textit{Strickler} window. Here you can create couples of Strickler coefficients, the first one for the minor bed, the second one for the medium bed. Click on \textit{add} four times to create four new couple. Give them the folowing values:
|
You fist have to define sets of Strickler coefficients. Click on \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"} to open the \textit{Strickler} window. Here you can create couples of Strickler coefficients, the first one for the minor bed, the second one for the medium bed. Click on \textit{add} four times to create four new couple. Give them the folowing values:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\includegraphics[width=15cm]{K.png}
|
\includegraphics[width=15cm]{K.png}
|
||||||
\par\end{center}
|
\par\end{center}
|
||||||
|
|
||||||
Close the \textit{Strickler} window. On the \textit{Edit friction} window, add four lines with the button \includegraphics[width=0.5cm]{gtk_add.png} to create fous friction zones. Each zone is defined by a \textit{begin} and \textit{end} PK and a \textit{begin} and \textit{end} Strickler couple. The strickler coefficient couples inside a zone are interpolated from the \textit{begin} and \textit{end} couples. In our case, we will use constant coefficients per zone. Set the zones as follow:
|
Close the \textit{Strickler} window. On the \textit{Edit friction} window, add four lines with the button \includegraphics[width=0.5cm]{gtk_add.png} to create four friction zones. Each zone is defined by a \textit{begin} and \textit{end} PK and a \textit{begin} and \textit{end} Strickler couple. The strickler coefficient couples inside a zone are interpolated from the \textit{begin} and \textit{end} couples. In our case, we will use constant coefficients per zone. Set the zones as follow:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\includegraphics[width=15cm]{frictions.png}
|
\includegraphics[width=15cm]{frictions.png}
|
||||||
\par\end{center}
|
\par\end{center}
|
||||||
|
|
||||||
|
The selected zone is highlighted in blue. Close the \textit{Edit friction} window.
|
||||||
|
|
||||||
|
|
||||||
|
\section{Model hydraulic structures}
|
||||||
|
|
||||||
|
Sometimes there can be cross-sections in which Shallow water equations can not be used to model the water flow. In that case, we have to define an other law to link the water elevation and the flow discharge. This is the case, for example, under bridges when the water elevation is too high, leading to a flow in charge. Pamhyr2 enables to define various hydraulic structures with laws that can be parametrized. In our case, a weir and two bridges have to be represented as hydraulic structures.
|
||||||
|
From the main window, click on \texttt{[Hydraulics] => [Hydraulic structures]} to open the hydraulic structures window. Click tree times on the \includegraphics[width=0.5cm]{gtk_add.png} button to create three hydraulic structures. Each structure can have a name and must have a reach and a kp. Set them as follow:
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=15cm]{hs.png}
|
||||||
|
\par\end{center}
|
||||||
|
|
||||||
|
Select the downstream weir and click on \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"} to edit the laws of this structure. Hydraulic structures are composed of basic hydraulic structures. You can combine the laws of several basic hydraulic structures to setup your structure. For this weir, we only need a weir basic hydraulic structure. Click on \includegraphics[width=0.5cm]{gtk_add.png} to add a new basic hydraulic structure, give it the \textit{weir} type and set it up as folow:
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=15cm]{seuil.png}
|
||||||
|
\par\end{center}
|
||||||
|
|
||||||
|
Go back to the \textit{hydraulic structures} window.
|
||||||
|
|
||||||
|
Select the RS101 bridge and click on \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"} to edit the laws of this structure. A bridge can be modeled as a combination of an orifice for the flow under the bridge and a weir for the flow over the bridge. Create two basic hydraulic structures and set them as folow:
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=15cm]{seuilRS101.png}
|
||||||
|
|
||||||
|
\includegraphics[width=15cm]{orificeRS101.png}
|
||||||
|
\par\end{center}
|
||||||
|
|
||||||
|
Go back to the \textit{hydraulic structures} window and aply the same procedure for the Thivencelle bridge:
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=15cm]{orificeThivencelle.png}
|
||||||
|
|
||||||
|
\includegraphics[width=15cm]{seuilThivencelle.png}
|
||||||
|
\par\end{center}
|
||||||
|
|
||||||
|
You can now close the \textit{hydraulic structures} windows.
|
||||||
|
|
||||||
|
\section{Solver parameters}
|
||||||
|
|
||||||
|
From the main window, click on \texttt{[Execute] => [Numerical parameters for solvers]}.
|
||||||
|
In the window \textit{solver parameters} select the \textit{Mage v8} tab. Set the solver parameters as follow:
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=15cm]{param.png}
|
||||||
|
\par\end{center}
|
||||||
|
|
||||||
|
Close the \textit{solver parameters} window.
|
||||||
|
|
||||||
|
\section{Run the simulation}
|
||||||
|
|
||||||
|
From the main window, click on \texttt{[Execute] => [Run solver]}.
|
||||||
|
Select \textit{Defaut-Mage - (Mage8)} anc click on the \textit{Run} button.
|
||||||
|
It will open two windows: the \textit{Check list} window and the \textit{Solver log} window.
|
||||||
|
The \textit{Check list} window gives som hints about the validity of your model, and the \textit{Solver log} window displays the outputs of the solver.
|
||||||
|
From the \textit{Solver log} window you can re-run the computation, and from the textit{Solver log} window you can click on the \textit{Results} button to open the \textit{Results} window.
|
||||||
|
|
||||||
|
\section{Visualize the results}
|
||||||
|
|
||||||
|
If you closed the textit{Solver log} window, you can click on \texttt{[Results] => [Visualize last results]} from the main window to open the \textit{Results} window. The top lets panel let you select your reach, the bottom left panel lets you select a cross-section in that reach. the tree plots on the right show the reach and the cross-section the same way than in the \textit{Geometry} window. You can use the bottom slider to visualize the water elevation at different timesteps.
|
||||||
|
To visualize the flow discharge, switch to the \textit{Hydrograph} tab.
|
||||||
|
To create custom 2D plots, click on the \includegraphics[width=0.5cm]{gtk_add.png} button on the top left of the window. Select the values you want on the $X$ and $Y$ axis and click on \textit{OK}.
|
||||||
|
You can now see a new tab with the custom 2D plot in the right panel of the \textit{Results} window.
|
||||||
|
|
||||||
|
|
||||||
\pagebreak{}
|
\pagebreak{}
|
||||||
\end{document}
|
\end{document}
|
||||||
|
|
||||||
@@ -16,10 +16,14 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Friction(SQLSubModel):
|
class Friction(SQLSubModel):
|
||||||
def __init__(self, name: str = "", status=None):
|
def __init__(self, name: str = "", status=None):
|
||||||
@@ -60,7 +64,8 @@ class Friction(SQLSubModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
reach = data["parent"] # Reach object
|
|
||||||
|
reach = data["reach"]
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
stricklers = data["stricklers"].stricklers
|
stricklers = data["stricklers"].stricklers
|
||||||
|
|
||||||
@@ -69,9 +74,6 @@ class Friction(SQLSubModel):
|
|||||||
f"FROM friction WHERE reach = {reach.id}"
|
f"FROM friction WHERE reach = {reach.id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
for _ in table:
|
|
||||||
new.append(None)
|
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
ind = row[0]
|
ind = row[0]
|
||||||
# Get stricklers
|
# Get stricklers
|
||||||
@@ -86,7 +88,9 @@ class Friction(SQLSubModel):
|
|||||||
sec.begin_strickler = bs
|
sec.begin_strickler = bs
|
||||||
sec.end_strickler = es
|
sec.end_strickler = es
|
||||||
|
|
||||||
yield ind, sec
|
new.append((ind, sec))
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
ind = data["ind"]
|
ind = data["ind"]
|
||||||
@@ -116,6 +120,10 @@ class Friction(SQLSubModel):
|
|||||||
def edge(self):
|
def edge(self):
|
||||||
return self._edge
|
return self._edge
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reach(self):
|
||||||
|
return self._edge
|
||||||
|
|
||||||
@edge.setter
|
@edge.setter
|
||||||
def edge(self, edge):
|
def edge(self, edge):
|
||||||
self._edge = edge
|
self._edge = edge
|
||||||
|
|||||||
@@ -50,10 +50,12 @@ class FrictionList(PamhyrModelList):
|
|||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = cls(status=data['status'])
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
new._lst = Friction._db_load(
|
ilst = Friction._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
new._lst = list(map(lambda x: x[1], sorted(ilst)))
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from math import dist
|
from math import dist, sqrt, acos
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
@@ -116,7 +116,7 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
sl = self._sl.id if self._sl is not None else -1
|
sl = self._sl.id if self._sl is not None else -1
|
||||||
|
|
||||||
sql = (
|
sql = (
|
||||||
"INSERT OR REPLACE INTO " +
|
"INSERT INTO " +
|
||||||
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) " +
|
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{ind}, '{self._db_format(self._name)}', " +
|
f"{ind}, '{self._db_format(self._name)}', " +
|
||||||
@@ -204,3 +204,55 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
Euclidean distance between the two points
|
Euclidean distance between the two points
|
||||||
"""
|
"""
|
||||||
return dist((p1.x, p1.y, p1.z), (p2.x, p2.y, p2.z))
|
return dist((p1.x, p1.y, p1.z), (p2.x, p2.y, p2.z))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def angle(p1, p2, p3):
|
||||||
|
"""angle p1 p2 p3.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
p1: A XYZ Point
|
||||||
|
p2: A XYZ Point
|
||||||
|
p3: A XYZ Point
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
angle p1 p2 p3
|
||||||
|
"""
|
||||||
|
a = PointXYZ.distance(p1, p2)
|
||||||
|
b = PointXYZ.distance(p2, p3)
|
||||||
|
c = PointXYZ.distance(p1, p3)
|
||||||
|
return acos((a**2 + b**2 - c**2) / (2 * a * b))
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def triangle_height(p1, p2, p3):
|
||||||
|
"""triangle p1 p2 p3 height starting at p2.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
p1: A XYZ Point
|
||||||
|
p2: A XYZ Point
|
||||||
|
p3: A XYZ Point
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
triangle height starting at p2
|
||||||
|
"""
|
||||||
|
a = PointXYZ.distance(p1, p2)
|
||||||
|
b = PointXYZ.distance(p2, p3)
|
||||||
|
c = PointXYZ.distance(p1, p3)
|
||||||
|
h2 = a**2 - ((c**2 - b**2 + a**2) / 2*c)**2
|
||||||
|
return sqrt(h2)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def triangle_area(p1, p2, p3):
|
||||||
|
"""triangle p1 p2 p3 area.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
p1: A XYZ Point
|
||||||
|
p2: A XYZ Point
|
||||||
|
p3: A XYZ Point
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
triangle height starting at p2
|
||||||
|
"""
|
||||||
|
h = PointXYZ.triangle_height(p1, p2, p3)
|
||||||
|
d = PointXYZ.distance(p1, p3)
|
||||||
|
return h*d/2
|
||||||
@@ -214,31 +214,31 @@ class Profile(object):
|
|||||||
self.points.insert(index, point)
|
self.points.insert(index, point)
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete(self, indexes: int):
|
|
||||||
"""Delete points at index
|
def add_point(self, point:Point):
|
||||||
|
"""append point at end of profile.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
indexes: List of index of points.
|
point: The point.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
points = set(
|
self._points.append(point)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_i(self, indexes: list):
|
||||||
|
self._points = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda e: e[1],
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] in indexes,
|
lambda e: e[0] not in indexes,
|
||||||
enumerate(self.points)
|
enumerate(self.points)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.points = list(
|
|
||||||
filter(
|
|
||||||
lambda p: p not in points,
|
|
||||||
self.points
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_points(self, points):
|
def delete_points(self, points):
|
||||||
@@ -250,7 +250,7 @@ class Profile(object):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
self.points = list(
|
self._points = list(
|
||||||
filter(
|
filter(
|
||||||
lambda p: p not in points,
|
lambda p: p not in points,
|
||||||
self.points
|
self.points
|
||||||
@@ -286,7 +286,7 @@ class Profile(object):
|
|||||||
elif column == 'z':
|
elif column == 'z':
|
||||||
def predicate(p): return p.z
|
def predicate(p): return p.z
|
||||||
|
|
||||||
self.points = sorted(
|
self._points = sorted(
|
||||||
self.points,
|
self.points,
|
||||||
key=predicate,
|
key=predicate,
|
||||||
reverse=is_reversed
|
reverse=is_reversed
|
||||||
@@ -298,7 +298,7 @@ class Profile(object):
|
|||||||
if len(self.points) != len(indexes):
|
if len(self.points) != len(indexes):
|
||||||
logger.critical("Indexes list do not correspond to point list")
|
logger.critical("Indexes list do not correspond to point list")
|
||||||
|
|
||||||
self.points = list(
|
self._points = list(
|
||||||
map(
|
map(
|
||||||
lambda x: x[1],
|
lambda x: x[1],
|
||||||
sorted(
|
sorted(
|
||||||
@@ -351,3 +351,7 @@ class Profile(object):
|
|||||||
# Abstract method for width approximation
|
# Abstract method for width approximation
|
||||||
def width_approximation(self):
|
def width_approximation(self):
|
||||||
raise NotImplementedMethodeError(self, self.width_approximation)
|
raise NotImplementedMethodeError(self, self.width_approximation)
|
||||||
|
|
||||||
|
# Abstract method for width approximation
|
||||||
|
def get_water_limits(self, z):
|
||||||
|
raise NotImplementedMethodeError(self, self.get_water_limits)
|
||||||
@@ -115,9 +115,6 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
f"WHERE reach = {reach.id}"
|
f"WHERE reach = {reach.id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
for _ in table:
|
|
||||||
profiles.append(None)
|
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
id = row[0]
|
id = row[0]
|
||||||
ind = row[1]
|
ind = row[1]
|
||||||
@@ -132,7 +129,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
id=id, num=num,
|
id=id, num=num,
|
||||||
name=name, kp=kp,
|
name=name, kp=kp,
|
||||||
code1=code1, code2=code2,
|
code1=code1, code2=code2,
|
||||||
reach=data["parent"],
|
reach=reach,
|
||||||
status=status
|
status=status
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -151,10 +148,6 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
yield ind, new
|
yield ind, new
|
||||||
|
|
||||||
# profiles[ind] = new
|
|
||||||
|
|
||||||
# return profiles
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
ok = True
|
ok = True
|
||||||
ind = data["ind"]
|
ind = data["ind"]
|
||||||
@@ -191,11 +184,20 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
profile = None
|
profile = None
|
||||||
try:
|
try:
|
||||||
if len(header) == 0:
|
if len(header) == 0:
|
||||||
|
name = data[0]
|
||||||
|
kp = data[1]
|
||||||
|
reach = data[2]
|
||||||
|
status = data[3]
|
||||||
|
|
||||||
profile = cls(
|
profile = cls(
|
||||||
*data
|
id=-1,
|
||||||
|
name=name,
|
||||||
|
kp=kp,
|
||||||
|
reach=reach,
|
||||||
|
status=status
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
valid_header = {'name', 'reach', 'kp'}
|
valid_header = {'name', 'reach', 'kp', 'status'}
|
||||||
d = {}
|
d = {}
|
||||||
for i, v in enumerate(data):
|
for i, v in enumerate(data):
|
||||||
h = header[i].strip().lower().split(' ')[0]
|
h = header[i].strip().lower().split(' ')[0]
|
||||||
@@ -204,6 +206,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
profile = cls(**d)
|
profile = cls(**d)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
raise ClipboardFormatError(header, data)
|
raise ClipboardFormatError(header, data)
|
||||||
|
|
||||||
return profile
|
return profile
|
||||||
@@ -437,8 +440,135 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
return list(map(lambda s: s - constant, station))
|
return list(map(lambda s: s - constant, station))
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def length(self) -> list:
|
||||||
|
l = []
|
||||||
|
x = self.x()
|
||||||
|
y = self.y()
|
||||||
|
z = self.z()
|
||||||
|
for i in range(len(self.points)-1):
|
||||||
|
dx = x[i+1] - x[i]
|
||||||
|
dy = y[i+1] - y[i]
|
||||||
|
dz = z[i+1] - z[i]
|
||||||
|
l.append( np.sqrt(dx*dx + dy*dy + dz*dz) )
|
||||||
|
|
||||||
|
return l
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def cumul_length(self) -> list:
|
||||||
|
lcum = []
|
||||||
|
lc = 0.0
|
||||||
|
l = self.length()
|
||||||
|
for i in l:
|
||||||
|
lc = lc + i
|
||||||
|
lcum.append(lc)
|
||||||
|
|
||||||
|
return lcum
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def purge1(self, np) -> list:
|
||||||
|
|
||||||
|
points = self.points
|
||||||
|
if (self.number_points <= np): return points
|
||||||
|
while len(points) > np:
|
||||||
|
x1 = [point.x() for point in points]
|
||||||
|
y1 = [point.y() for point in points]
|
||||||
|
z1 = [point.z() for point in points]
|
||||||
|
l = [(x*x+y*y+z*z) for (x,y,z) in zip(x1,y1,z1)]
|
||||||
|
l2 = [(x + y) for (x, y) in zip(l[:-1], l[1:])]
|
||||||
|
imin = np.argmin(l2)+1
|
||||||
|
points.pop(imin)
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def purge2(self, np) -> list:
|
||||||
|
|
||||||
|
points = self.points
|
||||||
|
if (self.number_points <= np): return points
|
||||||
|
while len(points) > np:
|
||||||
|
l2 = []
|
||||||
|
for i in range(len(points)-2):
|
||||||
|
l2.append(PointXYZ.angle(points(i),points(i+1),points(i+2)))
|
||||||
|
imax = np.argmax(l2)+1
|
||||||
|
points.pop(imax)
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def purge3(self, np) -> list:
|
||||||
|
|
||||||
|
points = self.points
|
||||||
|
if (self.number_points <= np): return points
|
||||||
|
while len(points) > np:
|
||||||
|
l2 = []
|
||||||
|
for i in range(len(points)-2):
|
||||||
|
l2.append(PointXYZ.triangle_area(points(i),points(i+1),points(i+2)))
|
||||||
|
imin = np.argmin(l2)+1
|
||||||
|
points.pop(imin)
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
def width_approximation(self):
|
def width_approximation(self):
|
||||||
rg = self.get_point_by_name("rg")
|
rg = self.get_point_by_name("rg")
|
||||||
rd = self.get_point_by_name("rd")
|
rd = self.get_point_by_name("rd")
|
||||||
|
|
||||||
return abs(rg.dist(rd))
|
return abs(rg.dist(rd))
|
||||||
|
|
||||||
|
def get_water_limits(self, z):
|
||||||
|
# ====================================================================
|
||||||
|
# détermination des points limites RG et RD pour un niveau
|
||||||
|
# d'eau donné
|
||||||
|
#
|
||||||
|
# irg et ird sont les premiers indices en partant des rives
|
||||||
|
# gauche et droite qui correspondent à des points sous la
|
||||||
|
# surface de l'eau ptX et ptY sont les points interpolés où
|
||||||
|
# le plan d'eau intersecte le profil known_level est le
|
||||||
|
# niveau d'eau pour lequel on a obtenu irg, ird, ptX et ptY
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
# initialisation
|
||||||
|
irg = -1
|
||||||
|
ird = -1
|
||||||
|
|
||||||
|
for i in range(self.number_points):
|
||||||
|
if self.point(i).z <= z:
|
||||||
|
irg = i
|
||||||
|
|
||||||
|
for i in reversed(range(self.number_points)):
|
||||||
|
if self.point(i).z <= z:
|
||||||
|
ird = i
|
||||||
|
|
||||||
|
# interpolation des points ptX et ptY
|
||||||
|
if (irg < self.number_points - 1):
|
||||||
|
x = np.interp(
|
||||||
|
z,
|
||||||
|
[self.point(irg).z, self.point(irg + 1).z],
|
||||||
|
[self.point(irg).x, self.point(irg + 1).x]
|
||||||
|
)
|
||||||
|
y = np.interp(
|
||||||
|
z,
|
||||||
|
[self.point(irg).z, self.point(irg + 1).z],
|
||||||
|
[self.point(irg).y, self.point(irg + 1).y]
|
||||||
|
)
|
||||||
|
ptX = PointXYZ(x, y, z)
|
||||||
|
else:
|
||||||
|
ptX = self.point(0)
|
||||||
|
if (ird > 0):
|
||||||
|
x = np.interp(
|
||||||
|
z,
|
||||||
|
[self.point(ird-1).z, self.point(ird).z],
|
||||||
|
[self.point(ird-1).x, self.point(ird).x]
|
||||||
|
)
|
||||||
|
y = np.interp(
|
||||||
|
z,
|
||||||
|
[self.point(ird).z, self.point(ird - 1).z],
|
||||||
|
[self.point(ird).y, self.point(ird - 1).y]
|
||||||
|
)
|
||||||
|
ptY = PointXYZ(x, y, z)
|
||||||
|
else:
|
||||||
|
ptY = self.point(self.number_points - 1)
|
||||||
|
|
||||||
|
return ptX, ptY
|
||||||
@@ -61,7 +61,7 @@ class Reach(SQLSubModel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = cls(status=data["status"], parent=data["parent"])
|
new = cls(status=data["status"], parent=data["reach"])
|
||||||
|
|
||||||
new._profiles = ProfileXYZ._db_load(
|
new._profiles = ProfileXYZ._db_load(
|
||||||
execute,
|
execute,
|
||||||
@@ -579,6 +579,266 @@ class Reach(SQLSubModel):
|
|||||||
file_st.write(" 999.9990 999.9990 999.9990")
|
file_st.write(" 999.9990 999.9990 999.9990")
|
||||||
file_st.write("\n")
|
file_st.write("\n")
|
||||||
|
|
||||||
|
# Interpolate
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def interpolate_step(self, lim: list, guideline1: str, guideline2: str, step: float, longmi: int, linear: bool):
|
||||||
|
"""interpolate with a given step between profile lim[0] and profile lim[1]
|
||||||
|
Args:
|
||||||
|
lim: index of profiles for interpolation
|
||||||
|
guideline1: first guideline for distance
|
||||||
|
guideline2: second guideline for distance
|
||||||
|
step: interpolation step
|
||||||
|
longmi: 1 if distance computed on min(dist(guideline1),dist(guideline2))
|
||||||
|
2 if distance computed on max(dist(guideline1),dist(guideline2))
|
||||||
|
else: distance computed on (dist(guideline1)+dist(guideline2))/2
|
||||||
|
linear: true if linear interpolation, false if spline interpolation
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of profiles including old profiles and interpolated profiles.
|
||||||
|
"""
|
||||||
|
if len(lim) != 2:
|
||||||
|
print('TODO: CRITICAL ERROR!') # error for now, only one interval
|
||||||
|
if lim[0] < 0 or lim[0] >= lim[1] or lim[1] >= self.number_profiles:
|
||||||
|
print('TODO: CRITICAL ERROR!')
|
||||||
|
if lim[1] < 0 or lim[0] > lim[1]:
|
||||||
|
print('TODO: CRITICAL ERROR!')
|
||||||
|
comp, inconmp = self.compute_guidelines()
|
||||||
|
if not guideline1 in comp:
|
||||||
|
print('TODO: CRITICAL ERROR!')
|
||||||
|
if not guideline2 in comp:
|
||||||
|
print('TODO: CRITICAL ERROR!')
|
||||||
|
|
||||||
|
nbsti = [] # number of profiles to add between self._profile[i] and self._profile[i+1]
|
||||||
|
for i in range(lim[0]):
|
||||||
|
nbsti.append(0)
|
||||||
|
for i in range(lim[0],lim[1]-1):
|
||||||
|
points = self.profile(i).points
|
||||||
|
point11 = next(filter(lambda p: p.name == guideline1, points))
|
||||||
|
point12 = next(filter(lambda p: p.name == guideline2, points))
|
||||||
|
points = self.profile(i+1).points
|
||||||
|
point21 = next(filter(lambda p: p.name == guideline1, points))
|
||||||
|
point22 = next(filter(lambda p: p.name == guideline2, points))
|
||||||
|
d1 = point11.dist(point21)
|
||||||
|
d2 = point12.dist(point22) # warning: 3D distance
|
||||||
|
|
||||||
|
if (longmi == 1):
|
||||||
|
d = max(d1, d2)
|
||||||
|
elif (longmi == 2):
|
||||||
|
d = min(d1, d2)
|
||||||
|
else:
|
||||||
|
d = 0.5*(d1 + d2)
|
||||||
|
|
||||||
|
nbsti[i] = nint(d / pas) - 1
|
||||||
|
if(nbsti[i] < 0): nbsti[i] = 0
|
||||||
|
|
||||||
|
for i in range(lim[1]-1,self.number_profiles-1):
|
||||||
|
nbsti.append(0)
|
||||||
|
|
||||||
|
return self.interpolate_nprof(nbsti, lineaire)
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def interpolate_nprof(self, nbsti: List[int], linear: bool):
|
||||||
|
"""add nbsti[i] profiles between self._profile[i] and self._profile[i+1]
|
||||||
|
|
||||||
|
Args:
|
||||||
|
nbsti: number of profiles to add between self._profile[i] and self._profile[i+1]
|
||||||
|
linear: true if linear interpolation, false if spline interpolation
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of profiles including old profiles and interpolated profiles.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if(lineaire):
|
||||||
|
return self.interpolate_linear(nbsti)
|
||||||
|
else:
|
||||||
|
# we might add the spline interpolation later
|
||||||
|
print('TODO: CRITICAL ERROR!')
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def interpolate_linear(self, nbsti: List[int]):
|
||||||
|
"""add nbsti[i] profiles between self._profile[i] and self._profile[i+1]
|
||||||
|
|
||||||
|
Args:
|
||||||
|
nbsti: number of profiles to add between self._profile[i] and self._profile[i+1]
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of profiles including old profiles and interpolated profiles.
|
||||||
|
"""
|
||||||
|
if len(nbsti) != self.number_profiles()-1:
|
||||||
|
print('TODO: CRITICAL ERROR!') # error for now, only one interval
|
||||||
|
sections_out = []
|
||||||
|
kps = self.get_kp()
|
||||||
|
for isect_in in range(len(nbsti)):
|
||||||
|
sections_out.append(self.profiles(isect_in))
|
||||||
|
dkp = kps[isect_in + 1] - kps[isect_in]
|
||||||
|
sect_in1 = self.profiles(isect_in)
|
||||||
|
sect_in2 = self.profiles(isect_in+1)
|
||||||
|
for isect_loc in range(nbsti[isect_in]):
|
||||||
|
# RATIO entre les deux sections initiales
|
||||||
|
d=float(isect_loc)/float(nbsti(isect_in)+1)
|
||||||
|
x1 = sect_in1.x()
|
||||||
|
y1 = sect_in1.y()
|
||||||
|
z1 = sect_in1.z()
|
||||||
|
x2 = sect_in2.x()
|
||||||
|
y2 = sect_in2.y()
|
||||||
|
z2 = sect_in2.z()
|
||||||
|
tags = sect_in1.names()
|
||||||
|
section_out.append(ProfileXYZ(kp = kps[isect_in] + d*dkp,
|
||||||
|
reach = self))
|
||||||
|
for i in range(len(x1)): # can do better...
|
||||||
|
# add the points to the created section
|
||||||
|
sections_out[-1].add_point(PointXYZ(x = x1[i] + d*(x2[i] - x1[i]),
|
||||||
|
y = y1[i] + d*(y2[i] - y1[i]),
|
||||||
|
z = z1[i] + d*(z2[i] - z1[i]),
|
||||||
|
name = tags[i]))
|
||||||
|
# don't forget the last one:
|
||||||
|
sections_out.append(self.profiles[self.number_profiles-1])
|
||||||
|
return sections_out
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def st_to_m(self):
|
||||||
|
"""convert a ST bief to a M bief (same number of points on every section)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
nbsti: number of profiles to add between self._profile[i] and self._profile[i+1]
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of profiles including old profiles and interpolated profiles.
|
||||||
|
"""
|
||||||
|
isectmax = 0
|
||||||
|
npoint_max = 0
|
||||||
|
new_profiles = []
|
||||||
|
np = []
|
||||||
|
nprof = len(self.number_profiles)
|
||||||
|
# find the profile with more points
|
||||||
|
for isect in range(nprof):
|
||||||
|
new_profiles.append(self.profile(isect))
|
||||||
|
points = new_profiles[isect].points
|
||||||
|
np.append(len(points))
|
||||||
|
if(npoint_max < len(points)):
|
||||||
|
npoint_max = len(points)
|
||||||
|
isectmax = isect
|
||||||
|
|
||||||
|
# let's go ! up from isectmax
|
||||||
|
for isect in range(isectmax+1,nprof):
|
||||||
|
# TODO case new_profiles[isect].length() = 0.0
|
||||||
|
# TODO case new_profiles[isect-1].length() = 0.0
|
||||||
|
l = new_profiles[isect-1].length() # length of each segment
|
||||||
|
lcum = new_profiles[isect-1].cumul_length() # lcum[-1] is length
|
||||||
|
alpha = [i/lcum[-1] for i in lcum]
|
||||||
|
l = new_profiles[isect].length()
|
||||||
|
lcum = new_profiles[isect].cumul_length()
|
||||||
|
beta = [i/lcum[-1] for i in lcum]
|
||||||
|
for i in range(np[isect], npoints_max):
|
||||||
|
beta.append(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for k in range(np[isect]): # boucle sur les points à rajouter
|
||||||
|
trouve = False
|
||||||
|
for j0 in range(np[isect]-1):
|
||||||
|
if(trouve):break
|
||||||
|
undeplus=True
|
||||||
|
for j1 in range(j0, np[isect]-1):
|
||||||
|
if(undeplus):
|
||||||
|
if(beta[j1].le.alpha[j1]):
|
||||||
|
undeplus=False
|
||||||
|
break
|
||||||
|
|
||||||
|
if (undeplus):
|
||||||
|
if (j0 == np[isect]-2):
|
||||||
|
trouve=True
|
||||||
|
else:
|
||||||
|
j1=j0+1
|
||||||
|
if (beta[j0]-alpha[j0] > alpha[j1]-beta[j0]): # beta(j0) > (alpha(j1)+alpha(j2))/2
|
||||||
|
trouve = True
|
||||||
|
|
||||||
|
|
||||||
|
if (trouve):
|
||||||
|
# on rajoute un point
|
||||||
|
ratio=(alpha[j0]-beta[j0-1])/(beta[j0]-beta[j0-1])
|
||||||
|
if (ratio < 0.0):
|
||||||
|
# on double le point a gauche
|
||||||
|
new_profiles[isect].insert_point(j0,new_profiles[isect].points[j0])
|
||||||
|
beta[j0]=beta[j0-1]
|
||||||
|
new_profiles[isect].points[j0+1].name('')
|
||||||
|
else:
|
||||||
|
p = PointXYZ(x = (1.0-ratio)*new_profiles[isect].points[j0].x + ratio*new_profiles[isect].points[j0].x,
|
||||||
|
y = (1.0-ratio)*new_profiles[isect].points[j0].y + ratio*new_profiles[isect].points[j0].y,
|
||||||
|
z = (1.0-ratio)*new_profiles[isect].points[j0].z + ratio*new_profiles[isect].points[j0].z
|
||||||
|
)
|
||||||
|
new_profiles[isect].insert_point(j0,p)
|
||||||
|
|
||||||
|
n = len(new_profiles[isect].points)
|
||||||
|
if n <= npoint_max: # we add the missing points at the end of the profile
|
||||||
|
for k in range(n,npoints_max): new_profiles[isect].add_point(new_profiles[isect].points[-1])
|
||||||
|
|
||||||
|
|
||||||
|
# let's go ! down from isectmax
|
||||||
|
for isect in range(isectmax-1,-1,-1):
|
||||||
|
# TODO case new_profiles[isect].length() = 0.0
|
||||||
|
# TODO case new_profiles[isect-1].length() = 0.0
|
||||||
|
l = new_profiles[isect+1].length() # length of each segment
|
||||||
|
lcum = new_profiles[isect+1].cumul_length() # lcum[-1] is length
|
||||||
|
alpha = [i/lcum[-1] for i in lcum]
|
||||||
|
l = new_profiles[isect].length()
|
||||||
|
lcum = new_profiles[isect].cumul_length()
|
||||||
|
beta = [i/lcum[-1] for i in lcum]
|
||||||
|
for i in range(np[isect], npoints_max):
|
||||||
|
beta.append(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for k in range(np[isect]): # boucle sur les points à rajouter
|
||||||
|
trouve = False
|
||||||
|
for j0 in range(np[isect]-1):
|
||||||
|
if(trouve):break
|
||||||
|
undeplus=True
|
||||||
|
for j1 in range(j0, np[isect]-1):
|
||||||
|
if(undeplus):
|
||||||
|
if(beta[j1].le.alpha[j1]):
|
||||||
|
undeplus=False
|
||||||
|
break
|
||||||
|
|
||||||
|
if (undeplus):
|
||||||
|
if (j0 == np[isect]-2):
|
||||||
|
trouve=True
|
||||||
|
else:
|
||||||
|
j1=j0+1
|
||||||
|
if (beta[j0]-alpha[j0] > alpha[j1]-beta[j0]): # beta(j0) > (alpha(j1)+alpha(j2))/2
|
||||||
|
trouve = True
|
||||||
|
|
||||||
|
|
||||||
|
if (trouve):
|
||||||
|
# on rajoute un point
|
||||||
|
ratio=(alpha[j0]-beta[j0-1])/(beta[j0]-beta[j0-1])
|
||||||
|
if (ratio < 0.0):
|
||||||
|
# on double le point a gauche
|
||||||
|
new_profiles[isect].insert_point(j0,new_profiles[isect].points[j0])
|
||||||
|
new_profiles[isect].points[j0+1].name('')
|
||||||
|
beta[j0]=beta[j0-1]
|
||||||
|
else:
|
||||||
|
p = PointXYZ(x = (1.0-ratio)*new_profiles[isect].points[j0].x + ratio*new_profiles[isect].points[j0].x,
|
||||||
|
y = (1.0-ratio)*new_profiles[isect].points[j0].y + ratio*new_profiles[isect].points[j0].y,
|
||||||
|
z = (1.0-ratio)*new_profiles[isect].points[j0].z + ratio*new_profiles[isect].points[j0].z,
|
||||||
|
)
|
||||||
|
new_profiles[isect].insert_point(j0,p)
|
||||||
|
|
||||||
|
n = len(new_profiles[isect].points)
|
||||||
|
if n <= npoint_max: # we add the missing points at the end of the profile
|
||||||
|
for k in range(n,npoints_max): new_profiles[isect].add_point(new_profiles[isect].points[-1])
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def purge(self, np: int) -> list:
|
||||||
|
profiles = []
|
||||||
|
for profile in self.profiles():
|
||||||
|
points = profile.purge2(np)
|
||||||
|
profiles.append(ProfileXYZ())
|
||||||
|
profiles[-1].import_points(points)
|
||||||
|
|
||||||
|
return profiles
|
||||||
|
|
||||||
def get_incline(self):
|
def get_incline(self):
|
||||||
first = self.profile(0)
|
first = self.profile(0)
|
||||||
last = self.profile(len(self) - 1)
|
last = self.profile(len(self) - 1)
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
# HydraulicStructures.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
from Model.HydraulicStructures.Basic.Value import (
|
||||||
|
BHSValue
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class BasicHS(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
BHSValue,
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(BasicHS, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = BasicHS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._type = ""
|
||||||
|
self._enabled = True
|
||||||
|
self._data = []
|
||||||
|
|
||||||
|
BasicHS._id_cnt = max(BasicHS._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE hydraulic_structures_basic(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
enabled BOOLEAN NOT NULL,
|
||||||
|
hs INTEGER,
|
||||||
|
FOREIGN KEY(hs) REFERENCES hydraulic_structures(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == minor == "0":
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_ctor_from_type(cls, t):
|
||||||
|
from Model.HydraulicStructures.Basic.Types import (
|
||||||
|
BHS_types, NotDefined,
|
||||||
|
)
|
||||||
|
|
||||||
|
res = NotDefined
|
||||||
|
|
||||||
|
if t in BHS_types.keys():
|
||||||
|
res = BHS_types[t]
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, name, type, enabled, hs " +
|
||||||
|
"FROM hydraulic_structures_basic " +
|
||||||
|
f"WHERE hs = {data['hs_id']} "
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in table:
|
||||||
|
bhs_id = row[0]
|
||||||
|
name = row[1]
|
||||||
|
type = row[2]
|
||||||
|
enabled = (row[3] == 1)
|
||||||
|
hs_id = row[4]
|
||||||
|
|
||||||
|
ctor = cls._get_ctor_from_type(type)
|
||||||
|
bhs = ctor(
|
||||||
|
id=bhs_id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
bhs.enabled = enabled
|
||||||
|
|
||||||
|
data['bhs_id'] = bhs_id
|
||||||
|
bhs._data = BHSValue._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
new.append(bhs)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
hs_id = data['hs_id']
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"hydraulic_structures_basic(id, name, type, enabled, hs) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, " +
|
||||||
|
f"'{self._db_format(self._name)}', " +
|
||||||
|
f"'{self._db_format(self._type)}', " +
|
||||||
|
f"{self._db_format(self.enabled)}, " +
|
||||||
|
f"{hs_id} " +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
data['bhs_id'] = self.id
|
||||||
|
execute(
|
||||||
|
"DELETE FROM hydraulic_structures_basic_value " +
|
||||||
|
f"WHERE bhs = {self.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for values in self._data:
|
||||||
|
values._db_save(execute, data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@type.setter
|
||||||
|
def type(self, type):
|
||||||
|
self._type = type
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parameters(self):
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
def convert(self, new_type):
|
||||||
|
return new_type(id=self.id, name=self.name, status=self._status)
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
# Types.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from Model.HydraulicStructures.Basic.HydraulicStructures import (
|
||||||
|
BasicHS
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.HydraulicStructures.Basic.Value import (
|
||||||
|
BHSValue
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NotDefined(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(NotDefined, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "ND"
|
||||||
|
self._data = []
|
||||||
|
|
||||||
|
|
||||||
|
class SeuilDeversoir(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(SeuilDeversoir, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "S1"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Largeur", float, 1.0, status=status),
|
||||||
|
BHSValue("Cote", float, 1.0, status=status),
|
||||||
|
BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SeuilTrapezoidal(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(SeuilTrapezoidal, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "S2"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Largeur", float, 1.0, status=status),
|
||||||
|
BHSValue("Cote", float, 1.0, status=status),
|
||||||
|
BHSValue("Cote de mise en charge", float, 9999.999, status=status),
|
||||||
|
BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
BHSValue("Tangeante du demi angle", float, 0.0, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SeuilTriangulaire(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(SeuilTriangulaire, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "S3"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Cote", float, 1.0, status=status),
|
||||||
|
BHSValue("Cote de mise en charge", float, 9999.999, status=status),
|
||||||
|
BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
BHSValue("Tangeante du demi angle", float, 0.0, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OrificeRectangulaire(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(OrificeRectangulaire, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "OR"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Largeur", float, 0.0, status=status),
|
||||||
|
BHSValue("Cote", float, 0.0, status=status),
|
||||||
|
BHSValue("Cote de mise en charge", float, 9999.999,
|
||||||
|
status=status),
|
||||||
|
BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
BHSValue("Cote de mise en charge maximale", float, 9999.999,
|
||||||
|
status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OrificeCirculaire(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(OrificeCirculaire, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "OC"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Diametre", float, 0.0, status=status),
|
||||||
|
BHSValue("Cote", float, 0.0, status=status),
|
||||||
|
BHSValue("hauteur envasement", float, 0.0, status=status),
|
||||||
|
BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# class OrificeVoute(BasicHS):
|
||||||
|
# def __init__(self, id: int = -1, name: str = "",
|
||||||
|
# status=None):
|
||||||
|
# super(OrificeVoute, self).__init__(
|
||||||
|
# id=id, name=name,
|
||||||
|
# status=status
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self._type = "OV"
|
||||||
|
# self._data = [
|
||||||
|
# BHSValue("Cote", float, 1.0, status=status),
|
||||||
|
# BHSValue("Largeur", float, 1.0, status=status),
|
||||||
|
# BHSValue("Haut de la voute", float, 0.0, status=status),
|
||||||
|
# BHSValue("Bas de la voute", float, 0.0, status=status),
|
||||||
|
# BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
# ]
|
||||||
|
|
||||||
|
|
||||||
|
class VanneRectangulaire(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(VanneRectangulaire, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "V1"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Largeur", float, 1.0, status=status),
|
||||||
|
BHSValue("Cote", float, 0.0, status=status),
|
||||||
|
BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
BHSValue("Ouverture", float, 1.0, status=status),
|
||||||
|
BHSValue("Ouverture maximale", float, 1.0, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class VanneRectangulaireSimplifiee(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(VanneRectangulaireSimplifiee, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "V2"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Largeur", float, 1.0, status=status),
|
||||||
|
BHSValue("Cote", float, 0.0, status=status),
|
||||||
|
BHSValue("Coefficient de debit", float, 0.4, status=status),
|
||||||
|
BHSValue("Ouverture", float, 1.0, status=status),
|
||||||
|
BHSValue("Ouverture maximale", float, 1.0, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Borda(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(Borda, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "BO"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Pas espace", float, 0.1, status=status),
|
||||||
|
BHSValue("Seuil", float, 0.15, status=status),
|
||||||
|
BHSValue("Coefficient", float, 0.4, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class UserDefined(BasicHS):
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(UserDefined, self).__init__(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
self._type = "UD"
|
||||||
|
self._data = [
|
||||||
|
BHSValue("Parameter 1", float, 0.0, status=status),
|
||||||
|
BHSValue("Parameter 2", float, 0.0, status=status),
|
||||||
|
BHSValue("Parameter 3", float, 0.0, status=status),
|
||||||
|
BHSValue("Parameter 4", float, 0.0, status=status),
|
||||||
|
BHSValue("Parameter 5", float, 0.0, status=status),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
BHS_types = {
|
||||||
|
"ND": NotDefined,
|
||||||
|
"S1": SeuilDeversoir,
|
||||||
|
"S2": SeuilTrapezoidal,
|
||||||
|
"S3": SeuilTriangulaire,
|
||||||
|
"OR": OrificeRectangulaire,
|
||||||
|
"OC": OrificeCirculaire,
|
||||||
|
# "OV": OrificeVoute,
|
||||||
|
"V1": VanneRectangulaire,
|
||||||
|
"V2": VanneRectangulaireSimplifiee,
|
||||||
|
"BO": Borda,
|
||||||
|
"UD": UserDefined
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
# Value.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
|
||||||
|
|
||||||
|
class BHSValue(SQLSubModel):
|
||||||
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, name: str = "", type=float, value=0.0,
|
||||||
|
status=None):
|
||||||
|
super(BHSValue, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._type = type
|
||||||
|
self._value = type(value)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE hydraulic_structures_basic_value(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
bhs INTEGER,
|
||||||
|
FOREIGN KEY(bhs) REFERENCES hydraulic_structures_basic(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == minor == "0":
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _str_to_type(cls, type):
|
||||||
|
res = str
|
||||||
|
|
||||||
|
if type == "float":
|
||||||
|
res = float
|
||||||
|
elif type == "int":
|
||||||
|
res = int
|
||||||
|
elif type == "bool":
|
||||||
|
res = bool
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _type_to_str(cls, type):
|
||||||
|
res = "str"
|
||||||
|
|
||||||
|
if type == float:
|
||||||
|
res = "float"
|
||||||
|
elif type == int:
|
||||||
|
res = "int"
|
||||||
|
elif type == bool:
|
||||||
|
res = "bool"
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
bhs_id = data["bhs_id"]
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT name, type, value " +
|
||||||
|
"FROM hydraulic_structures_basic_value " +
|
||||||
|
f"WHERE bhs = '{bhs_id}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in table:
|
||||||
|
name = row[0]
|
||||||
|
type = cls._str_to_type(row[1])
|
||||||
|
value = row[2]
|
||||||
|
|
||||||
|
val = cls(
|
||||||
|
name=name,
|
||||||
|
type=type,
|
||||||
|
value=value,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
new.append(val)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
bhs_id = data["bhs_id"]
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"hydraulic_structures_basic_value(name, type, value, bhs) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"'{self._db_format(self._name)}', " +
|
||||||
|
f"'{self._db_format(self._type_to_str(self._type))}', " +
|
||||||
|
f"'{self._db_format(self._value)}', " +
|
||||||
|
f"{bhs_id}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
self._value = self._type(value)
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
# HydraulicStructures.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
from Model.HydraulicStructures.Basic.HydraulicStructures import BasicHS
|
||||||
|
from Model.HydraulicStructures.Basic.Types import (
|
||||||
|
NotDefined,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class HydraulicStructure(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
BasicHS,
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(HydraulicStructure, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = HydraulicStructure._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._input_kp = None
|
||||||
|
self._output_kp = None
|
||||||
|
self._input_reach = None
|
||||||
|
self._output_reach = None
|
||||||
|
self._enabled = True
|
||||||
|
self._data = []
|
||||||
|
|
||||||
|
HydraulicStructure._id_cnt = max(
|
||||||
|
HydraulicStructure._id_cnt + 1,
|
||||||
|
self.id
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE hydraulic_structures(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
enabled BOOLEAN NOT NULL,
|
||||||
|
input_kp REAL NOT NULL,
|
||||||
|
output_kp REAL NOT NULL,
|
||||||
|
input_reach INTEGER,
|
||||||
|
output_reach INTEGER,
|
||||||
|
FOREIGN KEY(input_reach) REFERENCES river_reach(id),
|
||||||
|
FOREIGN KEY(output_reach) REFERENCES river_reach(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == minor == "0":
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, name, enabled, " +
|
||||||
|
"input_kp, output_kp, " +
|
||||||
|
"input_reach, output_reach " +
|
||||||
|
"FROM hydraulic_structures "
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in table:
|
||||||
|
it = iter(row)
|
||||||
|
|
||||||
|
hs_id = next(it)
|
||||||
|
name = next(it)
|
||||||
|
enabled = (next(it) == 1)
|
||||||
|
input_kp = next(it)
|
||||||
|
output_kp = next(it)
|
||||||
|
input_reach_id = next(it)
|
||||||
|
output_reach_id = next(it)
|
||||||
|
|
||||||
|
hs = cls(
|
||||||
|
id=hs_id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
hs.enabled = enabled
|
||||||
|
hs.input_kp = input_kp if input_kp != -1 else None
|
||||||
|
hs.output_kp = output_kp if output_kp != -1 else None
|
||||||
|
|
||||||
|
hs.input_reach, hs.output_reach = reduce(
|
||||||
|
lambda acc, n: (
|
||||||
|
n if n.id == input_reach_id else acc[0],
|
||||||
|
n if n.id == output_reach_id else acc[1]
|
||||||
|
),
|
||||||
|
data["edges"],
|
||||||
|
[None, None]
|
||||||
|
)
|
||||||
|
|
||||||
|
data['hs_id'] = hs_id
|
||||||
|
hs._data = BasicHS._db_load(execute, data)
|
||||||
|
|
||||||
|
new.append(hs)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute(f"DELETE FROM hydraulic_structures WHERE id = {self.id}")
|
||||||
|
|
||||||
|
input_reach_id = -1
|
||||||
|
if self._input_reach is not None:
|
||||||
|
input_reach_id = self._input_reach.id
|
||||||
|
|
||||||
|
output_reach_id = -1
|
||||||
|
if self._output_reach is not None:
|
||||||
|
output_reach_id = self._output_reach.id
|
||||||
|
|
||||||
|
input_kp = -1
|
||||||
|
if self.input_kp is not None:
|
||||||
|
input_kp = self.input_kp
|
||||||
|
|
||||||
|
output_kp = -1
|
||||||
|
if self.output_kp is not None:
|
||||||
|
output_kp = self.output_kp
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"hydraulic_structures(" +
|
||||||
|
" id, name, enabled, input_kp, output_kp, " +
|
||||||
|
" input_reach, output_reach" +
|
||||||
|
") " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
|
f"{self._db_format(self.enabled)}, " +
|
||||||
|
f"{input_kp}, {output_kp}, " +
|
||||||
|
f"{input_reach_id}, {output_reach_id}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
data['hs_id'] = self.id
|
||||||
|
execute(
|
||||||
|
"DELETE FROM hydraulic_structures_basic " +
|
||||||
|
f"WHERE hs = {self.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for basic in self._data:
|
||||||
|
basic._db_save(execute, data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def input_kp(self):
|
||||||
|
return self._input_kp
|
||||||
|
|
||||||
|
@input_kp.setter
|
||||||
|
def input_kp(self, input_kp):
|
||||||
|
self._input_kp = input_kp
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def output_kp(self):
|
||||||
|
return self._output_kp
|
||||||
|
|
||||||
|
@output_kp.setter
|
||||||
|
def output_kp(self, output_kp):
|
||||||
|
self._output_kp = output_kp
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def input_reach(self):
|
||||||
|
return self._input_reach
|
||||||
|
|
||||||
|
@input_reach.setter
|
||||||
|
def input_reach(self, input_reach):
|
||||||
|
self._input_reach = input_reach
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def output_reach(self):
|
||||||
|
return self._output_reach
|
||||||
|
|
||||||
|
@output_reach.setter
|
||||||
|
def output_reach(self, output_reach):
|
||||||
|
self._output_reach = output_reach
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def basic_structures(self):
|
||||||
|
return self._data.copy()
|
||||||
|
|
||||||
|
def basic_structure(self, index: int):
|
||||||
|
return self._data[index]
|
||||||
|
|
||||||
|
def add(self, index: int):
|
||||||
|
value = NotDefined(status=self._status)
|
||||||
|
self._data.insert(index, value)
|
||||||
|
self._status.modified()
|
||||||
|
return value
|
||||||
|
|
||||||
|
def insert(self, index: int, value: BasicHS):
|
||||||
|
self._data.insert(index, value)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def delete_i(self, indexes):
|
||||||
|
self._data = list(
|
||||||
|
map(
|
||||||
|
lambda e: e[1],
|
||||||
|
filter(
|
||||||
|
lambda e: e[0] not in indexes,
|
||||||
|
enumerate(self._data)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def delete(self, els):
|
||||||
|
self._data = list(
|
||||||
|
filter(
|
||||||
|
lambda e: e not in els,
|
||||||
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def sort(self, _reverse=False, key=None):
|
||||||
|
if key is None:
|
||||||
|
self._data.sort(reverse=_reverse)
|
||||||
|
else:
|
||||||
|
self._data.sort(reverse=_reverse, key=key)
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# HydraulicStructuresList.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.HydraulicStructures.HydraulicStructures import HydraulicStructure
|
||||||
|
|
||||||
|
|
||||||
|
class HydraulicStructureList(PamhyrModelList):
|
||||||
|
_sub_classes = [
|
||||||
|
HydraulicStructure,
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
new._lst = HydraulicStructure._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute("DELETE FROM hydraulic_structures")
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for hs in self._lst:
|
||||||
|
hs._db_save(execute, data=data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def new(self, lst, index):
|
||||||
|
n = HydraulicStructure(status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
def __copy__(self):
|
||||||
|
new = HydraulicStructureList()
|
||||||
|
|
||||||
|
for lst in self._tabs:
|
||||||
|
new.tabs[lst] = self._tabs[lst].copy()
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def __deepcopy__(self):
|
||||||
|
new = HydraulicStructureList()
|
||||||
|
|
||||||
|
for lst in self._tabs:
|
||||||
|
new.tabs[lst] = self._tabs[lst].deepcopy()
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return copy(self)
|
||||||
@@ -250,7 +250,7 @@ class InitialConditions(SQLSubModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if new._data is not None:
|
if new._data is not None:
|
||||||
yield new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
ok = True
|
ok = True
|
||||||
@@ -376,7 +376,7 @@ class InitialConditions(SQLSubModel):
|
|||||||
* (abs(incline) ** (0.5)))
|
* (abs(incline) ** (0.5)))
|
||||||
)
|
)
|
||||||
|
|
||||||
elevation= max(
|
elevation = max(
|
||||||
profile.z_min() + height,
|
profile.z_min() + height,
|
||||||
previous_elevation
|
previous_elevation
|
||||||
)
|
)
|
||||||
@@ -395,6 +395,7 @@ class InitialConditions(SQLSubModel):
|
|||||||
self._data.append(new)
|
self._data.append(new)
|
||||||
previous_elevation = elevation
|
previous_elevation = elevation
|
||||||
|
|
||||||
|
self._generate_resort_data(profiles)
|
||||||
|
|
||||||
def generate_discharge(self, discharge: float):
|
def generate_discharge(self, discharge: float):
|
||||||
self._data = []
|
self._data = []
|
||||||
@@ -421,7 +422,7 @@ class InitialConditions(SQLSubModel):
|
|||||||
((width * 0.8) * strickler * (abs(incline) ** (0.5)))
|
((width * 0.8) * strickler * (abs(incline) ** (0.5)))
|
||||||
) ** (0.6)
|
) ** (0.6)
|
||||||
|
|
||||||
elevation= max(
|
elevation = max(
|
||||||
profile.z_min() + height,
|
profile.z_min() + height,
|
||||||
previous_elevation
|
previous_elevation
|
||||||
)
|
)
|
||||||
@@ -438,3 +439,15 @@ class InitialConditions(SQLSubModel):
|
|||||||
|
|
||||||
previous_elevation = elevation
|
previous_elevation = elevation
|
||||||
self._data.append(new)
|
self._data.append(new)
|
||||||
|
|
||||||
|
self._generate_resort_data(profiles)
|
||||||
|
|
||||||
|
def _generate_resort_data(self, profiles):
|
||||||
|
is_reverse = False
|
||||||
|
if profiles[0].kp > profiles[-1].kp:
|
||||||
|
is_reverse = True
|
||||||
|
|
||||||
|
self._data.sort(
|
||||||
|
reverse=not is_reverse,
|
||||||
|
key=lambda d: d['kp']
|
||||||
|
)
|
||||||
@@ -212,3 +212,16 @@ class Graph(object):
|
|||||||
|
|
||||||
def is_enable_edge(self, edge):
|
def is_enable_edge(self, edge):
|
||||||
return edge._enable
|
return edge._enable
|
||||||
|
|
||||||
|
# def get_edge_id(self, reach):
|
||||||
|
# for i, e in enumerate(self.enable_edges):
|
||||||
|
# if e.id == reach.id:
|
||||||
|
# return i
|
||||||
|
|
||||||
|
def get_edge_id(self, reach):
|
||||||
|
return next(
|
||||||
|
filter(
|
||||||
|
lambda e: e[1].id == reach.id,
|
||||||
|
enumerate(self.enable_edges())
|
||||||
|
)
|
||||||
|
)[0]
|
||||||
@@ -94,7 +94,11 @@ class Reservoir(SQLSubModel):
|
|||||||
|
|
||||||
new_reservoir._node = None
|
new_reservoir._node = None
|
||||||
if node_id != -1:
|
if node_id != -1:
|
||||||
new_reservoir._node = next(filter(lambda n: n.id == node_id, data["nodes"]))
|
new_reservoir._node = next(
|
||||||
|
filter(
|
||||||
|
lambda n: n.id == node_id, data["nodes"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
new_data = []
|
new_data = []
|
||||||
table = execute(
|
table = execute(
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ from Model.Friction.FrictionList import FrictionList
|
|||||||
from Model.SolverParameters.SolverParametersList import SolverParametersList
|
from Model.SolverParameters.SolverParametersList import SolverParametersList
|
||||||
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
|
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
|
||||||
from Model.Reservoir.ReservoirList import ReservoirList
|
from Model.Reservoir.ReservoirList import ReservoirList
|
||||||
|
from Model.HydraulicStructures.HydraulicStructuresList import (
|
||||||
|
HydraulicStructureList,
|
||||||
|
)
|
||||||
|
|
||||||
from Solver.Solvers import solver_type_list
|
from Solver.Solvers import solver_type_list
|
||||||
|
|
||||||
@@ -154,7 +157,9 @@ class RiverReach(Edge, SQLSubModel):
|
|||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT id, name, enable, node1, node2 FROM river_reach")
|
"SELECT id, name, enable, node1, node2 FROM river_reach"
|
||||||
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
# Update id counter
|
# Update id counter
|
||||||
cls._id_cnt = max(cls._id_cnt, row[0])
|
cls._id_cnt = max(cls._id_cnt, row[0])
|
||||||
@@ -169,10 +174,9 @@ class RiverReach(Edge, SQLSubModel):
|
|||||||
new = cls(id, name, node1, node2, status=data["status"])
|
new = cls(id, name, node1, node2, status=data["status"])
|
||||||
new.enable(enable=enable)
|
new.enable(enable=enable)
|
||||||
|
|
||||||
data["reach"] = id
|
data["reach"] = new
|
||||||
data["parent"] = new
|
|
||||||
new._reach = Reach._db_load(execute, data)
|
|
||||||
|
|
||||||
|
new._reach = Reach._db_load(execute, data)
|
||||||
new._frictions = FrictionList._db_load(execute, data)
|
new._frictions = FrictionList._db_load(execute, data)
|
||||||
|
|
||||||
reachs.append(new)
|
reachs.append(new)
|
||||||
@@ -219,6 +223,7 @@ class River(Graph, SQLSubModel):
|
|||||||
SolverParametersList,
|
SolverParametersList,
|
||||||
SedimentLayerList,
|
SedimentLayerList,
|
||||||
ReservoirList,
|
ReservoirList,
|
||||||
|
HydraulicStructureList,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, status=None):
|
def __init__(self, status=None):
|
||||||
@@ -237,6 +242,9 @@ class River(Graph, SQLSubModel):
|
|||||||
self._parameters = {}
|
self._parameters = {}
|
||||||
self._sediment_layers = SedimentLayerList(status=self._status)
|
self._sediment_layers = SedimentLayerList(status=self._status)
|
||||||
self._reservoir = ReservoirList(status=self._status)
|
self._reservoir = ReservoirList(status=self._status)
|
||||||
|
self._hydraulic_structures = HydraulicStructureList(
|
||||||
|
status=self._status
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute):
|
||||||
@@ -303,6 +311,12 @@ class River(Graph, SQLSubModel):
|
|||||||
data
|
data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Hydraulic Structures
|
||||||
|
new._hydraulic_structures = HydraulicStructureList._db_load(
|
||||||
|
execute,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
# Parameters
|
# Parameters
|
||||||
new._parameters = SolverParametersList._db_load(
|
new._parameters = SolverParametersList._db_load(
|
||||||
execute,
|
execute,
|
||||||
@@ -319,6 +333,7 @@ class River(Graph, SQLSubModel):
|
|||||||
objs.append(self._sediment_layers)
|
objs.append(self._sediment_layers)
|
||||||
objs.append(self._stricklers)
|
objs.append(self._stricklers)
|
||||||
objs.append(self._reservoir)
|
objs.append(self._reservoir)
|
||||||
|
objs.append(self._hydraulic_structures)
|
||||||
|
|
||||||
for solver in self._parameters:
|
for solver in self._parameters:
|
||||||
objs.append(self._parameters[solver])
|
objs.append(self._parameters[solver])
|
||||||
@@ -363,6 +378,10 @@ class River(Graph, SQLSubModel):
|
|||||||
def reservoir(self):
|
def reservoir(self):
|
||||||
return self._reservoir
|
return self._reservoir
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hydraulic_structures(self):
|
||||||
|
return self._hydraulic_structures
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parameters(self):
|
def parameters(self):
|
||||||
return self._parameters
|
return self._parameters
|
||||||
|
|||||||
@@ -142,6 +142,10 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
insert(v[0], v[1], ind)
|
insert(v[0], v[1], ind)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
|
if int(release) < 7:
|
||||||
|
insert("mage_init_internal", "N", ind)
|
||||||
|
ind += 1
|
||||||
|
|
||||||
new = [
|
new = [
|
||||||
("mage_sediment_masse_volumique", "2650.0"),
|
("mage_sediment_masse_volumique", "2650.0"),
|
||||||
("mage_sediment_angle_repos", "40.0"),
|
("mage_sediment_angle_repos", "40.0"),
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Study(SQLModel):
|
|||||||
|
|
||||||
def __init__(self, filename=None, init_new=True):
|
def __init__(self, filename=None, init_new=True):
|
||||||
# Metadata
|
# Metadata
|
||||||
self._version = "0.0.5"
|
self._version = "0.0.7"
|
||||||
self.creation_date = datetime.now()
|
self.creation_date = datetime.now()
|
||||||
self.last_modification_date = datetime.now()
|
self.last_modification_date = datetime.now()
|
||||||
self.last_save_date = datetime.now()
|
self.last_save_date = datetime.now()
|
||||||
@@ -86,7 +86,7 @@ class Study(SQLModel):
|
|||||||
def is_saved(self):
|
def is_saved(self):
|
||||||
return self.status.is_saved()
|
return self.status.is_saved()
|
||||||
|
|
||||||
def save(self):
|
def save(self, progress=None):
|
||||||
# Save a copy of database
|
# Save a copy of database
|
||||||
fdir, fname = os.path.split(self.filename)
|
fdir, fname = os.path.split(self.filename)
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ class Study(SQLModel):
|
|||||||
|
|
||||||
# Save
|
# Save
|
||||||
self.last_save_date = datetime.now()
|
self.last_save_date = datetime.now()
|
||||||
self._save()
|
self._save(progress=progress)
|
||||||
self.status.save()
|
self.status.save()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -266,38 +266,54 @@ class Study(SQLModel):
|
|||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _save(self):
|
def _save(self, progress=None):
|
||||||
|
progress = progress if progress is not None else lambda: None
|
||||||
|
|
||||||
self.execute(
|
self.execute(
|
||||||
f"UPDATE info SET " +
|
f"UPDATE info SET " +
|
||||||
f"value='{self._db_format(self.name)}' WHERE key='name'"
|
f"value='{self._db_format(self.name)}' WHERE key='name'"
|
||||||
)
|
)
|
||||||
|
progress()
|
||||||
self.execute(
|
self.execute(
|
||||||
f"UPDATE info SET " +
|
f"UPDATE info SET " +
|
||||||
f"value='{self._db_format(self.description)}' " +
|
f"value='{self._db_format(self.description)}' " +
|
||||||
"WHERE key='description'"
|
"WHERE key='description'"
|
||||||
)
|
)
|
||||||
|
progress()
|
||||||
self.execute(
|
self.execute(
|
||||||
f"UPDATE info SET " +
|
f"UPDATE info SET " +
|
||||||
f"value='{self._time_system}' WHERE key='time_system'"
|
f"value='{self._time_system}' WHERE key='time_system'"
|
||||||
)
|
)
|
||||||
|
progress()
|
||||||
self.execute(
|
self.execute(
|
||||||
f"UPDATE info SET " +
|
f"UPDATE info SET " +
|
||||||
f"value='{timestamp(self._date)}' WHERE key='date'"
|
f"value='{timestamp(self._date)}' WHERE key='date'"
|
||||||
)
|
)
|
||||||
|
progress()
|
||||||
self.execute(
|
self.execute(
|
||||||
f"UPDATE info SET " +
|
f"UPDATE info SET " +
|
||||||
f"value='{timestamp(self.creation_date)}' " +
|
f"value='{timestamp(self.creation_date)}' " +
|
||||||
"WHERE key='creation_date'"
|
"WHERE key='creation_date'"
|
||||||
)
|
)
|
||||||
|
progress()
|
||||||
self.execute(
|
self.execute(
|
||||||
f"UPDATE info SET " +
|
f"UPDATE info SET " +
|
||||||
f"value='{timestamp(self.last_save_date)}' " +
|
f"value='{timestamp(self.last_save_date)}' " +
|
||||||
"WHERE key='last_save_date'"
|
"WHERE key='last_save_date'"
|
||||||
)
|
)
|
||||||
|
progress()
|
||||||
|
|
||||||
self._save_submodel([self._river])
|
self._save_submodel([self._river], data=progress)
|
||||||
self.commit()
|
self.commit()
|
||||||
|
|
||||||
|
def sql_save_request_count(self):
|
||||||
|
return self._count()
|
||||||
|
|
||||||
|
def _count(self):
|
||||||
|
cnt = self._save_count([self._river])
|
||||||
|
logger.debug(cnt)
|
||||||
|
return cnt + 6
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Close db connection
|
"""Close db connection
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import sqlite3
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
from tools import SQL
|
from tools import SQL
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
@@ -87,11 +88,16 @@ class SQLModel(SQL):
|
|||||||
raise NotImplementedMethodeError(self, self._update)
|
raise NotImplementedMethodeError(self, self._update)
|
||||||
|
|
||||||
def _save_submodel(self, objs, data=None):
|
def _save_submodel(self, objs, data=None):
|
||||||
def fn(sql): return self.execute(
|
progress = data if data is not None else lambda: None
|
||||||
sql,
|
|
||||||
fetch_one=False,
|
def fn(sql):
|
||||||
commit=False
|
res = self.execute(
|
||||||
)
|
sql,
|
||||||
|
fetch_one=False,
|
||||||
|
commit=False
|
||||||
|
)
|
||||||
|
progress()
|
||||||
|
return res
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
@@ -100,9 +106,43 @@ class SQLModel(SQL):
|
|||||||
self.commit()
|
self.commit()
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def _save(self):
|
def _save(self, progress=None):
|
||||||
raise NotImplementedMethodeError(self, self._save)
|
raise NotImplementedMethodeError(self, self._save)
|
||||||
|
|
||||||
|
def _count(self):
|
||||||
|
raise NotImplementedMethodeError(self, self._count)
|
||||||
|
|
||||||
|
def _save_count(self, objs, data=None):
|
||||||
|
counter = {
|
||||||
|
"insert": 0,
|
||||||
|
"update": 0,
|
||||||
|
"delete": 0,
|
||||||
|
"other": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
def fn(sql):
|
||||||
|
if "insert" in sql.lower():
|
||||||
|
counter["insert"] = counter["insert"] + 1
|
||||||
|
elif "update" in sql.lower():
|
||||||
|
counter["update"] = counter["update"] + 1
|
||||||
|
elif "delete" in sql.lower():
|
||||||
|
counter["delete"] = counter["delete"] + 1
|
||||||
|
else:
|
||||||
|
counter["other"] = counter["other"] + 1
|
||||||
|
return []
|
||||||
|
|
||||||
|
ok = True
|
||||||
|
for obj in objs:
|
||||||
|
ok &= obj._db_save(fn)
|
||||||
|
|
||||||
|
logger.debug(counter)
|
||||||
|
|
||||||
|
return reduce(
|
||||||
|
lambda acc, k: acc + counter[k],
|
||||||
|
counter,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load(cls, filename=None):
|
def _load(cls, filename=None):
|
||||||
raise NotImplementedMethodeError(cls, cls._load)
|
raise NotImplementedMethodeError(cls, cls._load)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class PamhyrModelDict(SQLSubModel):
|
|||||||
if key in self._dict:
|
if key in self._dict:
|
||||||
v = self._dict[key]
|
v = self._dict[key]
|
||||||
|
|
||||||
if type(v) == types.GeneratorType:
|
if type(v) is types.GeneratorType:
|
||||||
return list(v)
|
return list(v)
|
||||||
|
|
||||||
return v
|
return v
|
||||||
|
|||||||
@@ -0,0 +1,289 @@
|
|||||||
|
# MageMesh.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ctypes import (
|
||||||
|
cdll,
|
||||||
|
byref, Structure,
|
||||||
|
c_char_p, c_wchar_p,
|
||||||
|
create_string_buffer,
|
||||||
|
POINTER, c_void_p,
|
||||||
|
c_int, c_double, c_bool
|
||||||
|
)
|
||||||
|
|
||||||
|
from Scripts.AScript import AScript
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
bief_lib = None
|
||||||
|
c_init_bief_from_geo_file = None
|
||||||
|
c_get_nb_sections = None
|
||||||
|
c_get_nb_points_section = None
|
||||||
|
c_set_bief_name = None
|
||||||
|
c_st_to_m_compl = None
|
||||||
|
c_interpolate_profils_pas_transversal = None
|
||||||
|
c_update_pk = None
|
||||||
|
c_output_bief = None
|
||||||
|
|
||||||
|
##########################
|
||||||
|
# Binding initialisation #
|
||||||
|
##########################
|
||||||
|
|
||||||
|
|
||||||
|
def init_clib(lib_path):
|
||||||
|
global bief_lib
|
||||||
|
bief_lib = cdll.LoadLibrary(lib_path)
|
||||||
|
|
||||||
|
init_c_init_bief_from_geo_file(bief_lib)
|
||||||
|
init_c_get_nb_sections(bief_lib)
|
||||||
|
init_c_get_nb_points_section(bief_lib)
|
||||||
|
init_c_set_bief_name(bief_lib)
|
||||||
|
init_c_st_to_m_compl(bief_lib)
|
||||||
|
init_c_interpolate_profils_pas_transversal(bief_lib)
|
||||||
|
init_c_update_pk(bief_lib)
|
||||||
|
init_c_output_bief(bief_lib)
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_init_bief_from_geo_file(bief_lib):
|
||||||
|
global c_init_bief_from_geo_file
|
||||||
|
|
||||||
|
c_init_bief_from_geo_file = getattr(
|
||||||
|
bief_lib, 'c_init_bief_from_geo_file'
|
||||||
|
)
|
||||||
|
c_init_bief_from_geo_file.argtypes = [
|
||||||
|
c_char_p, POINTER(c_int), POINTER(c_int)
|
||||||
|
]
|
||||||
|
c_init_bief_from_geo_file.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_get_nb_sections(bief_lib):
|
||||||
|
global c_get_nb_sections
|
||||||
|
|
||||||
|
c_get_nb_sections = getattr(bief_lib, 'c_get_nb_sections')
|
||||||
|
c_get_nb_sections.argtypes = [POINTER(c_int)]
|
||||||
|
c_get_nb_sections.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_get_nb_points_section(bief_lib):
|
||||||
|
global c_get_nb_points_section
|
||||||
|
|
||||||
|
c_get_nb_points_section = getattr(bief_lib, 'c_get_nb_points_section')
|
||||||
|
c_get_nb_points_section.argtypes = [POINTER(c_int), POINTER(c_int)]
|
||||||
|
c_get_nb_points_section.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_set_bief_name(bief_lib):
|
||||||
|
global c_set_bief_name
|
||||||
|
|
||||||
|
c_set_bief_name = getattr(bief_lib, 'c_set_bief_name')
|
||||||
|
c_set_bief_name.argtypes = [c_char_p]
|
||||||
|
c_set_bief_name.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_st_to_m_compl(bief_lib):
|
||||||
|
global c_st_to_m_compl
|
||||||
|
|
||||||
|
c_st_to_m_compl = getattr(bief_lib, 'c_st_to_m_compl')
|
||||||
|
c_st_to_m_compl.argtypes = [POINTER(c_int), c_char_p, c_char_p]
|
||||||
|
c_st_to_m_compl.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_interpolate_profils_pas_transversal(bief_lib):
|
||||||
|
global c_interpolate_profils_pas_transversal
|
||||||
|
|
||||||
|
c_interpolate_profils_pas_transversal = getattr(
|
||||||
|
bief_lib, 'c_interpolate_profils_pas_transversal'
|
||||||
|
)
|
||||||
|
c_interpolate_profils_pas_transversal.argtypes = [
|
||||||
|
POINTER(c_int), POINTER(c_int),
|
||||||
|
c_char_p, c_char_p,
|
||||||
|
POINTER(c_double), POINTER(c_bool),
|
||||||
|
POINTER(c_int), POINTER(c_bool)
|
||||||
|
]
|
||||||
|
c_interpolate_profils_pas_transversal.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_update_pk(bief_lib):
|
||||||
|
global c_update_pk
|
||||||
|
|
||||||
|
c_update_pk = getattr(bief_lib, 'c_update_pk')
|
||||||
|
c_update_pk.argtypes = [c_char_p]
|
||||||
|
c_update_pk.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_c_output_bief(bief_lib):
|
||||||
|
global c_output_bief
|
||||||
|
|
||||||
|
c_output_bief = getattr(bief_lib, 'c_output_bief')
|
||||||
|
c_output_bief.argtypes = [c_char_p]
|
||||||
|
c_output_bief.restype = None
|
||||||
|
|
||||||
|
|
||||||
|
def init_bief_from_geo_file(name, with_charriage, with_water):
|
||||||
|
logger.info("! call init_bief_from_geo_file:")
|
||||||
|
cname = create_string_buffer(name.encode())
|
||||||
|
c_init_bief_from_geo_file(
|
||||||
|
cname,
|
||||||
|
byref(c_int(with_charriage)),
|
||||||
|
byref(c_int(with_water))
|
||||||
|
)
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# Binding functions #
|
||||||
|
#####################
|
||||||
|
|
||||||
|
|
||||||
|
def get_nb_sections():
|
||||||
|
nb_sections = c_int(0)
|
||||||
|
c_get_nb_sections(byref(nb_sections))
|
||||||
|
return nb_sections.value
|
||||||
|
|
||||||
|
|
||||||
|
def get_nb_points_section(section):
|
||||||
|
nb_points = c_int(0)
|
||||||
|
c_get_nb_points_section(byref(c_int(section)), byref(nb_points))
|
||||||
|
return nb_points.value
|
||||||
|
|
||||||
|
|
||||||
|
def set_bief_name(name):
|
||||||
|
logger.info(f"! call set_bief_name: {repr(name)}")
|
||||||
|
cname = create_string_buffer(name.encode())
|
||||||
|
c_set_bief_name(cname)
|
||||||
|
|
||||||
|
|
||||||
|
def st_to_m_compl(npoints, tag1=' ', tag2=' '):
|
||||||
|
logger.info(f"! call st_to_m_compl: {npoints}")
|
||||||
|
cnpoints = c_int(npoints)
|
||||||
|
ctag1 = create_string_buffer(tag1.encode())
|
||||||
|
ctag2 = create_string_buffer(tag2.encode())
|
||||||
|
|
||||||
|
c_st_to_m_compl(byref(cnpoints), ctag1, ctag2)
|
||||||
|
|
||||||
|
|
||||||
|
def interpolate_profils_pas_transversal(limite1, limite2,
|
||||||
|
directrice1, directrice2,
|
||||||
|
pas, lplan=False,
|
||||||
|
lm=3, lineaire=False):
|
||||||
|
logger.info("! call interpolate_profils_pas_transversal:")
|
||||||
|
climite1 = c_int(limite1)
|
||||||
|
climite2 = c_int(limite2)
|
||||||
|
cpas = c_double(pas)
|
||||||
|
clplan = c_bool(lplan)
|
||||||
|
clm = c_int(lm)
|
||||||
|
clineaire = c_bool(lineaire)
|
||||||
|
cdirectrice1 = create_string_buffer(directrice1.encode())
|
||||||
|
cdirectrice2 = create_string_buffer(directrice2.encode())
|
||||||
|
|
||||||
|
c_interpolate_profils_pas_transversal(
|
||||||
|
byref(climite1), byref(climite2),
|
||||||
|
cdirectrice1, cdirectrice2,
|
||||||
|
byref(cpas), byref(clplan),
|
||||||
|
byref(clm), byref(clineaire)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_pk(directrice):
|
||||||
|
logger.info("! call update_pk:")
|
||||||
|
cdirectrice = create_string_buffer(directrice.encode())
|
||||||
|
c_update_pk(cdirectrice)
|
||||||
|
|
||||||
|
|
||||||
|
def output_bief(name):
|
||||||
|
logger.info("! call output_bief:")
|
||||||
|
cname = create_string_buffer(name.encode())
|
||||||
|
c_output_bief(cname)
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Script #
|
||||||
|
##########
|
||||||
|
|
||||||
|
|
||||||
|
class MageMesh(AScript):
|
||||||
|
name = "MageMesh"
|
||||||
|
description = "Mesh ST file to M"
|
||||||
|
|
||||||
|
def usage(self):
|
||||||
|
logger.info(
|
||||||
|
f"Usage : {self._args[0]} {self._args[1]} " +
|
||||||
|
"<SO_FILE> <ST_FILE> <STEP>"
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if len(self._args) < 5:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
so = self._args[2]
|
||||||
|
st = self._args[3]
|
||||||
|
step = float(self._args[4])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Argument format error: {e}")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
try:
|
||||||
|
init_clib(so)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Bindings failed: {e}")
|
||||||
|
return 3
|
||||||
|
|
||||||
|
self.meshing(st, step)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def meshing(self, st_file: str, step: float):
|
||||||
|
workdir = self.get_workdir(st_file)
|
||||||
|
file_name = st_file.rsplit(".", 1)[0]
|
||||||
|
reach_name = os.path.basename(file_name)
|
||||||
|
|
||||||
|
# Open
|
||||||
|
init_bief_from_geo_file(st_file, 0, 0)
|
||||||
|
set_bief_name(reach_name)
|
||||||
|
|
||||||
|
ns = get_nb_sections()
|
||||||
|
npts_max = max(
|
||||||
|
map(
|
||||||
|
lambda i: get_nb_points_section(i),
|
||||||
|
range(1, ns)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Transform
|
||||||
|
st_to_m_compl(npts_max, 'rg', 'rd')
|
||||||
|
interpolate_profils_pas_transversal(
|
||||||
|
1, ns,
|
||||||
|
'un', 'np',
|
||||||
|
step
|
||||||
|
)
|
||||||
|
update_pk("un")
|
||||||
|
|
||||||
|
# Save
|
||||||
|
logger.info(f"Saved meshing geometry to {file_name}.M")
|
||||||
|
output_bief(f"{file_name}.M")
|
||||||
|
|
||||||
|
def get_workdir(self, file):
|
||||||
|
workdir = os.path.abspath(
|
||||||
|
os.path.dirname(file)
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(workdir, exist_ok=True)
|
||||||
|
logger.info(f"Set working dir to {workdir}")
|
||||||
|
|
||||||
|
return workdir
|
||||||
@@ -121,6 +121,9 @@ class AbstractSolver(object):
|
|||||||
def is_stoped(self):
|
def is_stoped(self):
|
||||||
return self._status == STATUS.STOPED
|
return self._status == STATUS.STOPED
|
||||||
|
|
||||||
|
def has_results_loaded(self):
|
||||||
|
self._status = STATUS.NOT_LAUNCHED
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ class CommandLineSolver(AbstractSolver):
|
|||||||
return os.path.abspath(
|
return os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
"..", ".."
|
"..", "..", ".."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -172,12 +172,13 @@ class CommandLineSolver(AbstractSolver):
|
|||||||
|
|
||||||
if not os.path.exists(exe):
|
if not os.path.exists(exe):
|
||||||
error = f"[ERROR] Path {exe} do not exists"
|
error = f"[ERROR] Path {exe} do not exists"
|
||||||
logger.info(error)
|
logger.warning(error)
|
||||||
return error
|
return error
|
||||||
|
|
||||||
self._process.start(
|
self._process.start(
|
||||||
exe, args,
|
exe, args,
|
||||||
)
|
)
|
||||||
|
self._process.waitForStarted()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -191,7 +192,7 @@ class CommandLineSolver(AbstractSolver):
|
|||||||
|
|
||||||
if not os.path.exists(exe):
|
if not os.path.exists(exe):
|
||||||
error = f"[ERROR] Path {exe} do not exists"
|
error = f"[ERROR] Path {exe} do not exists"
|
||||||
logger.info(error)
|
logger.warning(error)
|
||||||
return error
|
return error
|
||||||
|
|
||||||
self._process.start(
|
self._process.start(
|
||||||
@@ -212,37 +213,48 @@ class CommandLineSolver(AbstractSolver):
|
|||||||
|
|
||||||
if not os.path.exists(exe):
|
if not os.path.exists(exe):
|
||||||
error = f"[ERROR] Path {exe} do not exists"
|
error = f"[ERROR] Path {exe} do not exists"
|
||||||
logger.info(error)
|
logger.warning(error)
|
||||||
return error
|
return error
|
||||||
|
|
||||||
self._process.start(
|
self._process.start(
|
||||||
exe, args,
|
exe, args,
|
||||||
)
|
)
|
||||||
|
self._process.waitForStarted()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_ready(self):
|
def _data_ready(self):
|
||||||
# Read process output and put lines in queue
|
# Read process output and put lines in queue
|
||||||
s = self._process.readAll().data().decode()
|
s = self._process.readAll().data().decode()
|
||||||
|
|
||||||
if self._output is not None:
|
if self._output is not None:
|
||||||
for x in s.split('\n'):
|
for x in s.split('\n'):
|
||||||
self._output.put(x)
|
self._output.put(x)
|
||||||
|
|
||||||
def _run_next(self, study):
|
|
||||||
self._step += 1
|
|
||||||
if self._step < len(self._runs):
|
|
||||||
res = self._runs[self._step](study)
|
|
||||||
if res is not True:
|
|
||||||
self._output.put(res)
|
|
||||||
else:
|
|
||||||
self._status = STATUS.STOPED
|
|
||||||
|
|
||||||
def _finished(self, study, exit_code, exit_status):
|
def _finished(self, study, exit_code, exit_status):
|
||||||
if self._output is not None:
|
if self._output is not None:
|
||||||
self._output.put(exit_code)
|
self._output.put(exit_code)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"Process finished with " +
|
||||||
|
f"code: {exit_code}, status: {exit_status}"
|
||||||
|
)
|
||||||
|
|
||||||
self._run_next(study)
|
self._run_next(study)
|
||||||
|
|
||||||
|
def _run_next(self, study):
|
||||||
|
self._step += 1
|
||||||
|
|
||||||
|
if self._step >= len(self._runs):
|
||||||
|
self._status = STATUS.STOPED
|
||||||
|
return
|
||||||
|
|
||||||
|
fn = self._runs[self._step]
|
||||||
|
res = fn(study)
|
||||||
|
|
||||||
|
if res is not True:
|
||||||
|
self._output.put(res)
|
||||||
|
|
||||||
def run(self, study, process=None, output_queue=None):
|
def run(self, study, process=None, output_queue=None):
|
||||||
self._study = study
|
self._study = study
|
||||||
|
|
||||||
@@ -255,7 +267,8 @@ class CommandLineSolver(AbstractSolver):
|
|||||||
# Connect / reconnect signal
|
# Connect / reconnect signal
|
||||||
self._process.readyRead.connect(self._data_ready)
|
self._process.readyRead.connect(self._data_ready)
|
||||||
self._process.finished.connect(
|
self._process.finished.connect(
|
||||||
lambda c, s: self._finished(study, c, s))
|
lambda c, s: self._finished(study, c, s)
|
||||||
|
)
|
||||||
|
|
||||||
# Prepare running step
|
# Prepare running step
|
||||||
self._runs = [
|
self._runs = [
|
||||||
@@ -265,6 +278,8 @@ class CommandLineSolver(AbstractSolver):
|
|||||||
]
|
]
|
||||||
self._step = 0
|
self._step = 0
|
||||||
|
|
||||||
|
self._status = STATUS.RUNNING
|
||||||
|
|
||||||
# Run first step
|
# Run first step
|
||||||
res = self._runs[0](study)
|
res = self._runs[0](study)
|
||||||
if res is not True:
|
if res is not True:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from tools import timer
|
from tools import timer, trace
|
||||||
|
|
||||||
from Solver.CommandLine import CommandLineSolver
|
from Solver.CommandLine import CommandLineSolver
|
||||||
from Checker.Mage import MageNetworkGraphChecker
|
from Checker.Mage import MageNetworkGraphChecker
|
||||||
@@ -36,7 +36,7 @@ def mage_file_open(filepath, mode):
|
|||||||
|
|
||||||
if "w" in mode:
|
if "w" in mode:
|
||||||
# Write header
|
# Write header
|
||||||
f.write("* This file is generate by PAMHYR, please don't modify\n")
|
f.write("* This file is generated by PAMHYR, please don't modify\n")
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@@ -81,6 +81,7 @@ class Mage(CommandLineSolver):
|
|||||||
("mage_compute_reach_volume_balance", "y"),
|
("mage_compute_reach_volume_balance", "y"),
|
||||||
("mage_max_reach_volume_balance", "0.001"),
|
("mage_max_reach_volume_balance", "0.001"),
|
||||||
("mage_min_reach_volume_to_check", "1000.0"),
|
("mage_min_reach_volume_to_check", "1000.0"),
|
||||||
|
("mage_init_internal", " "),
|
||||||
]
|
]
|
||||||
|
|
||||||
return lst
|
return lst
|
||||||
@@ -374,13 +375,7 @@ class Mage(CommandLineSolver):
|
|||||||
with mage_file_open(os.path.join(repertory, f"{name}.INI"), "w+") as f:
|
with mage_file_open(os.path.join(repertory, f"{name}.INI"), "w+") as f:
|
||||||
has_ini = False
|
has_ini = False
|
||||||
id = 1
|
id = 1
|
||||||
reachs = study.river.edges()
|
reachs = study.river.enable_edges()
|
||||||
reachs = list(
|
|
||||||
filter(
|
|
||||||
lambda e: e.is_enable(),
|
|
||||||
reachs
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO put real date...
|
# TODO put real date...
|
||||||
f.write(f"$ date en minutes : 0.00\n")
|
f.write(f"$ date en minutes : 0.00\n")
|
||||||
@@ -397,13 +392,13 @@ class Mage(CommandLineSolver):
|
|||||||
|
|
||||||
id_sec = 1
|
id_sec = 1
|
||||||
for d in data:
|
for d in data:
|
||||||
IR = f"{id:>3}"
|
IR = f"{id}"
|
||||||
IS = f"{id_sec:>3}"
|
IS = f"{id_sec}"
|
||||||
discharge = f"{d['discharge']:>10.5f}"
|
discharge = f"{d['discharge']:>10.5f}"
|
||||||
elevation = f"{d['elevation']:>11.6f}"
|
elevation = f"{d['elevation']:>11.6f}"
|
||||||
kp = f"{d['kp']:>9.2f}"
|
kp = f"{d['kp']:>9.2f}"
|
||||||
|
|
||||||
f.write(f" {IR} {IS} {discharge}{elevation} {kp}\n")
|
f.write(f"{IR} {IS} {discharge} {elevation} {kp}\n")
|
||||||
id_sec += 1
|
id_sec += 1
|
||||||
|
|
||||||
id += 1
|
id += 1
|
||||||
@@ -442,13 +437,149 @@ class Mage(CommandLineSolver):
|
|||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def _export_SIN(self, study, repertory, qlog, name="0"):
|
||||||
|
files = []
|
||||||
|
|
||||||
|
sin_dict = {
|
||||||
|
"ND": "*",
|
||||||
|
"S1": "D", "S2": "T", "S3": "T",
|
||||||
|
"OR": "O", "OC": "B", "OV": "F",
|
||||||
|
"V1": "V", "V2": "W",
|
||||||
|
"BO": "A",
|
||||||
|
"UD": "X",
|
||||||
|
"PO": "P",
|
||||||
|
}
|
||||||
|
|
||||||
|
hydraulic_structures = study.river.hydraulic_structures.lst
|
||||||
|
if len(hydraulic_structures) == 0:
|
||||||
|
return files
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export SIN file")
|
||||||
|
|
||||||
|
with mage_file_open(os.path.join(repertory, f"{name}.SIN"), "w+") as f:
|
||||||
|
files.append(f"{name}.SIN")
|
||||||
|
|
||||||
|
for hs in hydraulic_structures:
|
||||||
|
if not hs.input_reach.is_enable():
|
||||||
|
continue
|
||||||
|
|
||||||
|
f.write(
|
||||||
|
'* ouvrage au pk ' +
|
||||||
|
f"{hs.input_kp:>12.1f}" + ' ' +
|
||||||
|
hs.name + '\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
for bhs in hs.basic_structures:
|
||||||
|
reach_id = study.river.get_edge_id(hs.input_reach) + 1
|
||||||
|
param_str = ' '.join(
|
||||||
|
[
|
||||||
|
f'{p:>10.3f}'
|
||||||
|
for p in self._export_SIN_parameters(bhs)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
f.write(
|
||||||
|
f"{sin_dict[bhs._type]} " +
|
||||||
|
f"{reach_id} {hs.input_kp:>12.3f} {param_str} " +
|
||||||
|
f"{bhs.name}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
def _export_SIN_parameters(self, bhs):
|
||||||
|
res = [9999.999] * 5
|
||||||
|
|
||||||
|
if len(bhs) == 5:
|
||||||
|
res = self._export_SIN_parameters_5(bhs)
|
||||||
|
elif len(bhs) == 4:
|
||||||
|
res = self._export_SIN_parameters_4(bhs)
|
||||||
|
elif len(bhs) == 3:
|
||||||
|
res = self._export_SIN_parameters_3(bhs)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _export_SIN_parameters_5(self, bhs):
|
||||||
|
# S2, OR, V1, V2, UD
|
||||||
|
return [
|
||||||
|
bhs._data[0].value,
|
||||||
|
bhs._data[1].value,
|
||||||
|
bhs._data[2].value,
|
||||||
|
bhs._data[3].value,
|
||||||
|
bhs._data[4].value,
|
||||||
|
]
|
||||||
|
|
||||||
|
def _export_SIN_parameters_4(self, bhs):
|
||||||
|
# S3, OC
|
||||||
|
res = [
|
||||||
|
bhs._data[0].value,
|
||||||
|
bhs._data[1].value,
|
||||||
|
bhs._data[2].value,
|
||||||
|
bhs._data[3].value,
|
||||||
|
0.0,
|
||||||
|
]
|
||||||
|
|
||||||
|
if bhs._type == "T": # S3
|
||||||
|
res = [0.0] + res[:-1]
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _export_SIN_parameters_3(self, bhs):
|
||||||
|
# S1, BO
|
||||||
|
if bhs._type == "S1":
|
||||||
|
res = [
|
||||||
|
bhs._data[0].value,
|
||||||
|
bhs._data[1].value,
|
||||||
|
0.0,
|
||||||
|
bhs._data[2].value,
|
||||||
|
9999.99,
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
res = [
|
||||||
|
bhs._data[0].value,
|
||||||
|
bhs._data[1].value,
|
||||||
|
bhs._data[2].value,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
]
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def _export_DEV(self, study, repertory, qlog, name="0"):
|
||||||
|
files = []
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export DEV file")
|
||||||
|
|
||||||
|
with mage_file_open(
|
||||||
|
os.path.join(
|
||||||
|
repertory, f"{name}.DEV"
|
||||||
|
), "w+"
|
||||||
|
) as f:
|
||||||
|
reachs = study.river.enable_edges()
|
||||||
|
|
||||||
|
id = 1
|
||||||
|
for reach in reachs:
|
||||||
|
f.write(f"YD{id:3}\n")
|
||||||
|
f.write(f"YG{id:3}\n")
|
||||||
|
id += 1
|
||||||
|
files.append(f"{name}.DEV")
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def _export_REP(self, study, repertory, files, qlog, name="0"):
|
def _export_REP(self, study, repertory, files, qlog, name="0"):
|
||||||
if qlog is not None:
|
if qlog is not None:
|
||||||
qlog.put("Export REP file")
|
qlog.put("Export REP file")
|
||||||
|
|
||||||
# Write header
|
# Write header
|
||||||
with mage_file_open(os.path.join(repertory, f"{name}.REP"), "w+") as f:
|
with mage_file_open(
|
||||||
|
os.path.join(
|
||||||
|
repertory, f"{name}.REP"
|
||||||
|
), "w+"
|
||||||
|
) as f:
|
||||||
f.write("confirmation=non\n")
|
f.write("confirmation=non\n")
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
@@ -588,8 +719,12 @@ class Mage8(Mage):
|
|||||||
if name in ["command_line_arguments"]:
|
if name in ["command_line_arguments"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if name == "mage_compute_reach_volume_balance":
|
if name == "compute_reach_volume_balance":
|
||||||
value = "O" if value == "y" else "N"
|
value = "O" if value.lower() == "y" else "N"
|
||||||
|
|
||||||
|
if name == "init_internal":
|
||||||
|
value = ("p" if value.lower() in ["y", "yes", "true"]
|
||||||
|
else "")
|
||||||
|
|
||||||
f.write(f"{name} {value}\n")
|
f.write(f"{name} {value}\n")
|
||||||
|
|
||||||
@@ -683,7 +818,9 @@ class Mage8(Mage):
|
|||||||
self._export_bound_cond(study, repertory, qlog, name=name)
|
self._export_bound_cond(study, repertory, qlog, name=name)
|
||||||
files = files + self._export_RUG(study, repertory, qlog, name=name)
|
files = files + self._export_RUG(study, repertory, qlog, name=name)
|
||||||
files = files + self._export_INI(study, repertory, qlog, name=name)
|
files = files + self._export_INI(study, repertory, qlog, name=name)
|
||||||
|
files = files + self._export_SIN(study, repertory, qlog, name=name)
|
||||||
files = files + self._export_CAS(study, repertory, qlog, name=name)
|
files = files + self._export_CAS(study, repertory, qlog, name=name)
|
||||||
|
files = files + self._export_DEV(study, repertory, qlog, name=name)
|
||||||
self._export_REP(study, repertory, files, qlog, name=name)
|
self._export_REP(study, repertory, files, qlog, name=name)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -813,6 +950,13 @@ class Mage8(Mage):
|
|||||||
|
|
||||||
# Set data for profile RI
|
# Set data for profile RI
|
||||||
reach.set(ri, timestamp, key, d)
|
reach.set(ri, timestamp, key, d)
|
||||||
|
if key == "Z":
|
||||||
|
profile = study.river\
|
||||||
|
.current_reach()\
|
||||||
|
.reach.profile(ri)
|
||||||
|
ptX, ptY = profile.get_water_limits(d)
|
||||||
|
reach.set(ri, timestamp, "ptX", ptX)
|
||||||
|
reach.set(ri, timestamp, "ptY", ptY)
|
||||||
|
|
||||||
endline()
|
endline()
|
||||||
end = newline().size <= 0
|
end = newline().size <= 0
|
||||||
@@ -855,7 +999,7 @@ class Mage8(Mage):
|
|||||||
logger.debug(f"read_gra: nb_profile = {nb_profile}")
|
logger.debug(f"read_gra: nb_profile = {nb_profile}")
|
||||||
logger.debug(f"read_gra: mage_version = {mage_version}")
|
logger.debug(f"read_gra: mage_version = {mage_version}")
|
||||||
|
|
||||||
if mage_version <= 80:
|
if mage_version < 80:
|
||||||
msg = (
|
msg = (
|
||||||
"Read GRA files: " +
|
"Read GRA files: " +
|
||||||
f"Possible incompatible mage version '{mage_version}', " +
|
f"Possible incompatible mage version '{mage_version}', " +
|
||||||
@@ -923,60 +1067,71 @@ class Mage8(Mage):
|
|||||||
def ip_to_ri(r, i): return i - reach_offset[r]
|
def ip_to_ri(r, i): return i - reach_offset[r]
|
||||||
|
|
||||||
ts = set()
|
ts = set()
|
||||||
ind = 0
|
|
||||||
end = False
|
end = False
|
||||||
|
|
||||||
newline()
|
newline()
|
||||||
while not end:
|
while not end:
|
||||||
n = read_int(1)[0]
|
n = read_int(1)[0]
|
||||||
timestamp = read_float64(1)[0]
|
timestamp = read_float64(1)[0]
|
||||||
|
with_bedload = read_int(1)[0]
|
||||||
|
|
||||||
logger.debug(f"read_gra: timestamp = {timestamp} sec")
|
logger.debug(f"read_gra: Number of cross section: {n}")
|
||||||
ts.add(timestamp)
|
logger.debug(f"read_gra: Timestamp: {timestamp}")
|
||||||
|
logger.debug(f"read_gra: Type of bedload: {with_bedload}")
|
||||||
|
|
||||||
endline()
|
endline()
|
||||||
|
|
||||||
for i in range(n):
|
npts = [1] * n
|
||||||
|
if with_bedload == 1:
|
||||||
newline()
|
newline()
|
||||||
nsl = read_int(1)[0]
|
npts = read_int(n)
|
||||||
endline()
|
endline()
|
||||||
|
sum_npts = sum(npts)
|
||||||
|
logger.debug(f"read_gra: Number of points: {sum_npts}")
|
||||||
|
|
||||||
# Get current profile id
|
newline()
|
||||||
|
nsl = read_int(sum_npts)
|
||||||
|
logger.debug(f"read_gra: Number of sedimentary layers: {nsl}")
|
||||||
|
endline()
|
||||||
|
|
||||||
|
newline()
|
||||||
|
data = read_float64(sum(nsl) * 3)
|
||||||
|
endline()
|
||||||
|
|
||||||
|
ts.add(timestamp)
|
||||||
|
|
||||||
|
i_pts = 0
|
||||||
|
i_data = 0
|
||||||
|
# Loop on cross section
|
||||||
|
for i in range(n):
|
||||||
|
sec_sl = []
|
||||||
reach = ip_to_r(i)
|
reach = ip_to_r(i)
|
||||||
ri = ip_to_ri(reach, i)
|
p_i = ip_to_ri(reach, i)
|
||||||
|
|
||||||
if nsl > 1:
|
|
||||||
logger.warning(
|
|
||||||
"read_gra: " +
|
|
||||||
"Multiple sediment layers for one profile " +
|
|
||||||
"is not implemented yet..."
|
|
||||||
)
|
|
||||||
|
|
||||||
for j in range(nsl):
|
|
||||||
newline()
|
|
||||||
nl = read_int(1)[0]
|
|
||||||
endline()
|
|
||||||
|
|
||||||
|
# Loop on cross section points
|
||||||
|
for j in range(npts[i]):
|
||||||
sl = []
|
sl = []
|
||||||
|
|
||||||
for k in range(nl):
|
# Loop on sediment layers
|
||||||
newline()
|
for k in range(nsl[i_pts]):
|
||||||
data = read_float64(3)
|
h = data[i_data]
|
||||||
endline()
|
d50 = data[i_data + 1]
|
||||||
|
sigma = data[i_data + 2]
|
||||||
h = data[0]
|
|
||||||
d50 = data[1]
|
|
||||||
sigma = data[2]
|
|
||||||
|
|
||||||
sl.append((h, d50, sigma))
|
sl.append((h, d50, sigma))
|
||||||
|
i_data += 3
|
||||||
|
|
||||||
reach.set(ri, timestamp, "sl", sl)
|
i_pts += 1
|
||||||
|
sec_sl.append(sl)
|
||||||
|
|
||||||
ind += 1
|
reach.set(p_i, timestamp, "sl", sec_sl)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f"read_gra: data size = {len(data)} ({i_data} readed)"
|
||||||
|
)
|
||||||
end = newline().size <= 0
|
end = newline().size <= 0
|
||||||
|
|
||||||
logger.debug(reachs[0].profiles[0]._data)
|
results.set("sediment_timestamps", ts)
|
||||||
results.set("timestamps", ts)
|
|
||||||
logger.info(f"read_gra: ... end with {len(ts)} timestamp read")
|
logger.info(f"read_gra: ... end with {len(ts)} timestamp read")
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class EditBoundaryConditionWindow(PamhyrWindow):
|
|||||||
table_headers=headers,
|
table_headers=headers,
|
||||||
editable_headers=self._data.header,
|
editable_headers=self._data.header,
|
||||||
delegates={
|
delegates={
|
||||||
#"time": self._delegate_time,
|
# "time": self._delegate_time,
|
||||||
},
|
},
|
||||||
data=self._data,
|
data=self._data,
|
||||||
undo=self._undo_stack,
|
undo=self._undo_stack,
|
||||||
|
|||||||
@@ -115,13 +115,13 @@ class ComboBoxDelegate(QItemDelegate):
|
|||||||
|
|
||||||
|
|
||||||
class TableModel(PamhyrTableModel):
|
class TableModel(PamhyrTableModel):
|
||||||
def __init__(self, trad = None, **kwargs):
|
def __init__(self, trad=None, **kwargs):
|
||||||
self._trad = trad
|
self._trad = trad
|
||||||
self._long_types = {}
|
self._long_types = {}
|
||||||
if self._trad is not None:
|
if self._trad is not None:
|
||||||
self._long_types = self._trad.get_dict("long_types")
|
self._long_types = self._trad.get_dict("long_types")
|
||||||
|
|
||||||
super(TableModel, self).__init__(trad = trad, **kwargs)
|
super(TableModel, self).__init__(trad=trad, **kwargs)
|
||||||
|
|
||||||
def _setup_lst(self):
|
def _setup_lst(self):
|
||||||
self._lst = self._data.boundary_condition
|
self._lst = self._data.boundary_condition
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class AddCommand(QUndoCommand):
|
|||||||
self._point = None
|
self._point = None
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
self._profile.delete([self._index])
|
self._profile.delete_i([self._index])
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
if self._point is None:
|
if self._point is None:
|
||||||
@@ -119,7 +119,7 @@ class DelCommand(QUndoCommand):
|
|||||||
self._profile.insert_point(row, point)
|
self._profile.insert_point(row, point)
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
self._profile.delete(self._rows)
|
self._profile.delete_i(self._rows)
|
||||||
|
|
||||||
|
|
||||||
class SortCommand(QUndoCommand):
|
class SortCommand(QUndoCommand):
|
||||||
@@ -181,7 +181,7 @@ class PasteCommand(QUndoCommand):
|
|||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
for ind in range(len(self._points)):
|
for ind in range(len(self._points)):
|
||||||
self._profile.delete([self._row])
|
self._profile.delete_i([self._row])
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
for point in self._points:
|
for point in self._points:
|
||||||
|
|||||||
@@ -135,16 +135,13 @@ class ProfileWindow(PamhyrWindow):
|
|||||||
self._tablemodel.blockSignals(False)
|
self._tablemodel.blockSignals(False)
|
||||||
|
|
||||||
def index_selected_row(self):
|
def index_selected_row(self):
|
||||||
rows = self._tablemodel\
|
table = self.find(QTableView, "tableView")
|
||||||
.selectionModel()\
|
rows = table.selectionModel()\
|
||||||
.selectedRows()
|
.selectedRows()
|
||||||
if len(rows) == 0:
|
if len(rows) == 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return self._tablemodel\
|
return rows[0].row()
|
||||||
.selectionModel()\
|
|
||||||
.selectedRows()[0]\
|
|
||||||
.row()
|
|
||||||
|
|
||||||
def add(self):
|
def add(self):
|
||||||
table = self.find(QTableView, "tableView")
|
table = self.find(QTableView, "tableView")
|
||||||
@@ -213,24 +210,24 @@ class ProfileWindow(PamhyrWindow):
|
|||||||
|
|
||||||
self.update_plot()
|
self.update_plot()
|
||||||
|
|
||||||
def copy(self):
|
def _copy(self):
|
||||||
rows = self._tablemodel\
|
table = self.find(QTableView, "tableView")
|
||||||
.selectionModel()\
|
rows = table.selectionModel().selectedRows()
|
||||||
.selectedRows()
|
|
||||||
table = []
|
data = []
|
||||||
table.append(["x", "y", "z", "name"])
|
data.append(["x", "y", "z", "name"])
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
point = self._profile.point(row.row())
|
point = self._profile.point(row.row())
|
||||||
table.append(
|
data.append(
|
||||||
[
|
[
|
||||||
point.x, point.y, point.z, point.name
|
point.x, point.y, point.z, point.name
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.copyTableIntoClipboard(table)
|
self.copyTableIntoClipboard(data)
|
||||||
|
|
||||||
def paste(self):
|
def _paste(self):
|
||||||
header, data = self.parseClipboardTable()
|
header, data = self.parseClipboardTable()
|
||||||
|
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
@@ -245,10 +242,10 @@ class ProfileWindow(PamhyrWindow):
|
|||||||
self._tablemodel.paste(row, header, data)
|
self._tablemodel.paste(row, header, data)
|
||||||
self.update_plot()
|
self.update_plot()
|
||||||
|
|
||||||
def undo(self):
|
def _undo(self):
|
||||||
self._tablemodel.undo()
|
self._tablemodel.undo()
|
||||||
self.update_plot()
|
self.update_plot()
|
||||||
|
|
||||||
def redo(self):
|
def _redo(self):
|
||||||
self._tablemodel.redo()
|
self._tablemodel.redo()
|
||||||
self.update_plot()
|
self.update_plot()
|
||||||
@@ -455,7 +455,7 @@ class GeometryWindow(PamhyrWindow):
|
|||||||
.selectedRows()
|
.selectedRows()
|
||||||
|
|
||||||
table = []
|
table = []
|
||||||
table.append(["name", "kp"])
|
# table.append(["name", "kp"])
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
profile = self._reach.profile(row.row())
|
profile = self._reach.profile(row.row())
|
||||||
@@ -471,13 +471,17 @@ class GeometryWindow(PamhyrWindow):
|
|||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(header) != 0:
|
# if len(header) != 0:
|
||||||
header.append("reach")
|
# header.append("reach")
|
||||||
|
# header.append("status")
|
||||||
|
|
||||||
for row in data:
|
for row in data:
|
||||||
row.append(self._reach)
|
row.append(self._reach)
|
||||||
|
row.append(self._study.river._status)
|
||||||
|
|
||||||
row = self.index_selected_row()
|
row = self.index_selected_row()
|
||||||
self._tablemodel.paste(row, header, data)
|
# self._tablemodel.paste(row, header, data)
|
||||||
|
self._tablemodel.paste(row, [], data)
|
||||||
self.select_current_profile()
|
self.select_current_profile()
|
||||||
|
|
||||||
def _undo(self):
|
def _undo(self):
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QMessageBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.HydraulicStructures.BasicHydraulicStructures.UndoCommand import (
|
||||||
|
SetNameCommand, SetTypeCommand,
|
||||||
|
SetEnabledCommand, AddCommand, DelCommand,
|
||||||
|
SetValueCommand,
|
||||||
|
)
|
||||||
|
from Model.HydraulicStructures.Basic.Types import BHS_types
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, trad=None, parent=None):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._trad = trad
|
||||||
|
|
||||||
|
self._long_types = {}
|
||||||
|
if self._trad is not None:
|
||||||
|
self._long_types = self._trad.get_dict("long_types")
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
lst = list(
|
||||||
|
map(
|
||||||
|
lambda k: self._long_types[k],
|
||||||
|
BHS_types.keys()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.editor.addItems(lst)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(index.data(Qt.DisplayRole))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def __init__(self, trad=None, **kwargs):
|
||||||
|
self._trad = trad
|
||||||
|
self._long_types = {}
|
||||||
|
if self._trad is not None:
|
||||||
|
self._long_types = self._trad.get_dict("long_types")
|
||||||
|
|
||||||
|
super(TableModel, self).__init__(trad=trad, **kwargs)
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
return self._data.basic_structure(row).name
|
||||||
|
elif self._headers[column] == "type":
|
||||||
|
return self._long_types[self._data.basic_structure(row).type]
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
self._undo.push(
|
||||||
|
SetNameCommand(
|
||||||
|
self._data, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "type":
|
||||||
|
if self._question_set_type():
|
||||||
|
key = next(
|
||||||
|
k for k, v in self._long_types.items()
|
||||||
|
if v == value
|
||||||
|
)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
SetTypeCommand(
|
||||||
|
self._data, row, BHS_types[key]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _question_set_type(self):
|
||||||
|
question = QMessageBox(self._parent)
|
||||||
|
|
||||||
|
question.setWindowTitle(self._trad['msg_type_change_title'])
|
||||||
|
question.setText(self._trad['msg_type_change_text'])
|
||||||
|
question.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
|
||||||
|
question.setIcon(QMessageBox.Question)
|
||||||
|
|
||||||
|
res = question.exec()
|
||||||
|
return res == QMessageBox.Ok
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._data, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._data, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def enabled(self, row, enabled, parent=QModelIndex()):
|
||||||
|
self._undo.push(
|
||||||
|
SetEnabledCommand(
|
||||||
|
self._lst, row, enabled
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
|
||||||
|
class ParametersTableModel(PamhyrTableModel):
|
||||||
|
def __init__(self, trad=None, **kwargs):
|
||||||
|
self._trad = trad
|
||||||
|
self._long_types = {}
|
||||||
|
|
||||||
|
if self._trad is not None:
|
||||||
|
self._long_types = self._trad.get_dict("long_types")
|
||||||
|
|
||||||
|
self._hs_index = None
|
||||||
|
|
||||||
|
super(ParametersTableModel, self).__init__(trad=trad, **kwargs)
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
if self._hs_index is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return len(
|
||||||
|
self._data.basic_structure(self._hs_index)
|
||||||
|
)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
if self._hs_index is None:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
hs = self._data.basic_structure(self._hs_index)
|
||||||
|
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
return hs.parameters[row].name
|
||||||
|
elif self._headers[column] == "value":
|
||||||
|
return str(hs.parameters[row].value)
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._hs_index is None:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "value":
|
||||||
|
self._undo.push(
|
||||||
|
SetValueCommand(
|
||||||
|
self._data.basic_structure(self._hs_index),
|
||||||
|
row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update_hs_index(self, index):
|
||||||
|
self._hs_index = index
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTranslate import PamhyrTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class BasicHydraulicStructuresTranslate(PamhyrTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(BasicHydraulicStructuresTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict['msg_type_change_title'] = _translate(
|
||||||
|
"BasicHydraulicStructures",
|
||||||
|
"Change hydraulic structure type"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._dict['msg_type_change_text'] = _translate(
|
||||||
|
"BasicHydraulicStructures",
|
||||||
|
"Do you want to change the hydraulic structure type and reset \
|
||||||
|
hydraulic structure values?"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sub_dict["long_types"] = {
|
||||||
|
"ND": _translate(
|
||||||
|
"BasicHydraulicStructures", "Not defined"
|
||||||
|
),
|
||||||
|
"S1": _translate(
|
||||||
|
"BasicHydraulicStructures", "Seuil déversoir"
|
||||||
|
),
|
||||||
|
"S2": _translate(
|
||||||
|
"BasicHydraulicStructures", "Seuil trapezoidal"
|
||||||
|
),
|
||||||
|
"S3": _translate(
|
||||||
|
"BasicHydraulicStructures", "Seuil triangulaire"
|
||||||
|
),
|
||||||
|
"OR": _translate(
|
||||||
|
"BasicHydraulicStructures", "Orifice rectangulaire"
|
||||||
|
),
|
||||||
|
"OC": _translate(
|
||||||
|
"BasicHydraulicStructures", "Orifice circulaire"
|
||||||
|
),
|
||||||
|
"OV": _translate(
|
||||||
|
"BasicHydraulicStructures", "Orifice voute"
|
||||||
|
),
|
||||||
|
"V1": _translate(
|
||||||
|
"BasicHydraulicStructures", "Vanne rectangulaire"
|
||||||
|
),
|
||||||
|
"V2": _translate(
|
||||||
|
"BasicHydraulicStructures", "Vanne rectangulaire simplifiée"
|
||||||
|
),
|
||||||
|
"BO": _translate(
|
||||||
|
"BasicHydraulicStructures", "Perte de charge à la Borda"
|
||||||
|
),
|
||||||
|
"UD": _translate(
|
||||||
|
"BasicHydraulicStructures", "User defined"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"name": _translate("BasicHydraulicStructures", "Name"),
|
||||||
|
"type": _translate("BasicHydraulicStructures", "Type"),
|
||||||
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_parameters"] = {
|
||||||
|
"name": _translate("BasicHydraulicStructures", "Name"),
|
||||||
|
"value": _translate("BasicHydraulicStructures", "Value"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SetNameCommand(QUndoCommand):
|
||||||
|
def __init__(self, hs, index, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._hs = hs
|
||||||
|
self._index = index
|
||||||
|
self._old = self._hs.basic_structure(self._index).name
|
||||||
|
self._new = str(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._hs.basic_structure(self._index).name = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._hs.basic_structure(self._index).name = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetTypeCommand(QUndoCommand):
|
||||||
|
def __init__(self, hs, index, new_type):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._hs = hs
|
||||||
|
self._index = index
|
||||||
|
self._type = new_type
|
||||||
|
self._old = self._hs.basic_structure(self._index)
|
||||||
|
self._new = self._hs.basic_structure(self._index)\
|
||||||
|
.convert(self._type)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._hs.delete_i([self._index])
|
||||||
|
self._hs.insert(self._index, self._old)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._hs.delete_i([self._index])
|
||||||
|
self._hs.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class SetEnabledCommand(QUndoCommand):
|
||||||
|
def __init__(self, hs, index, enabled):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._hs = hs
|
||||||
|
self._index = index
|
||||||
|
self._old = not enabled
|
||||||
|
self._new = enabled
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._hs.basic_structure(self._index).enabled = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._hs.basic_structure(self._index).enabled = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, hs, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._hs = hs
|
||||||
|
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._hs.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._hs.add(self._index)
|
||||||
|
else:
|
||||||
|
self._hs.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, hs, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._hs = hs
|
||||||
|
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._bhs = []
|
||||||
|
for row in rows:
|
||||||
|
self._bhs.append((row, self._hs.basic_structure(row)))
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._bhs:
|
||||||
|
self._hs.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._hs.delete_i(self._rows)
|
||||||
|
|
||||||
|
|
||||||
|
class PasteCommand(QUndoCommand):
|
||||||
|
def __init__(self, hs, row, h_s):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._hs = hs
|
||||||
|
|
||||||
|
self._row = row
|
||||||
|
self._bhs = deepcopy(h_s)
|
||||||
|
self._bhs.reverse()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._hs.delete_i(range(self._row, self._row + len(self._bhs)))
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for r in self._bhs:
|
||||||
|
self._hs.insert(self._row, r)
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Basic hydraulic structure values #
|
||||||
|
####################################
|
||||||
|
|
||||||
|
|
||||||
|
class SetValueCommand(QUndoCommand):
|
||||||
|
def __init__(self, bhs, index, value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._bhs = bhs
|
||||||
|
self._index = index
|
||||||
|
self._old = self._bhs.parameters[self._index].value
|
||||||
|
self._new = self._bhs.parameters[self._index].type(value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._bhs.parameters[self._index].value = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._bhs.parameters[self._index].value = self._new
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
|
pyqtSlot, pyqtSignal, QItemSelectionModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QHeaderView, QDoubleSpinBox, QVBoxLayout, QCheckBox
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
|
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||||
|
|
||||||
|
from View.HydraulicStructures.PlotAC import PlotAC
|
||||||
|
|
||||||
|
from View.HydraulicStructures.BasicHydraulicStructures.Table import (
|
||||||
|
ComboBoxDelegate, TableModel, ParametersTableModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Network.GraphWidget import GraphWidget
|
||||||
|
from View.HydraulicStructures.BasicHydraulicStructures.Translate import (
|
||||||
|
BasicHydraulicStructuresTranslate
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class BasicHydraulicStructuresWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "BasicHydraulicStructures"
|
||||||
|
_pamhyr_name = "Basic Hydraulic Structures"
|
||||||
|
|
||||||
|
def __init__(self, data=None, study=None, config=None, parent=None):
|
||||||
|
name = self._pamhyr_name + " - " + study.name
|
||||||
|
|
||||||
|
super(BasicHydraulicStructuresWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=BasicHydraulicStructuresTranslate(),
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(data)
|
||||||
|
|
||||||
|
self._hs = data
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_checkbox()
|
||||||
|
self.setup_plot()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self.setup_table_bhs()
|
||||||
|
self.setup_table_bhs_parameters()
|
||||||
|
|
||||||
|
def setup_table_bhs(self):
|
||||||
|
self._table = None
|
||||||
|
|
||||||
|
self._delegate_type = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["name", "type"],
|
||||||
|
delegates={
|
||||||
|
"type": self._delegate_type,
|
||||||
|
},
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._hs,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
parent=self,
|
||||||
|
)
|
||||||
|
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(0, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
def setup_table_bhs_parameters(self):
|
||||||
|
self._table_parameters = None
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView_2")
|
||||||
|
self._table_parameters = ParametersTableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers_parameters"),
|
||||||
|
editable_headers=["value"],
|
||||||
|
delegates={},
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._hs,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
parent=self,
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup_checkbox(self):
|
||||||
|
self._checkbox = self.find(QCheckBox, f"checkBox")
|
||||||
|
self._set_checkbox_state()
|
||||||
|
|
||||||
|
def setup_plot(self):
|
||||||
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas.setObjectName("canvas")
|
||||||
|
self.toolbar = PamhyrPlotToolbar(
|
||||||
|
self.canvas, self
|
||||||
|
)
|
||||||
|
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||||
|
self.plot_layout.addWidget(self.toolbar)
|
||||||
|
self.plot_layout.addWidget(self.canvas)
|
||||||
|
|
||||||
|
reach = self._hs.input_reach
|
||||||
|
profile_kp = self._hs.input_kp
|
||||||
|
if profile_kp is not None:
|
||||||
|
profiles = reach.reach.get_profiles_from_kp(float(profile_kp))
|
||||||
|
else:
|
||||||
|
profiles = None
|
||||||
|
if profiles is not None:
|
||||||
|
profile = profiles[0]
|
||||||
|
else:
|
||||||
|
profile = None
|
||||||
|
|
||||||
|
self.plot_ac = PlotAC(
|
||||||
|
canvas=self.canvas,
|
||||||
|
river=self._study.river,
|
||||||
|
reach=self._hs.input_reach,
|
||||||
|
profile=profile,
|
||||||
|
toolbar=self.toolbar
|
||||||
|
)
|
||||||
|
self.plot_ac.draw()
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_delete").triggered.connect(self.delete)
|
||||||
|
self._checkbox.clicked.connect(self._set_basic_structure_state)
|
||||||
|
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
table.selectionModel()\
|
||||||
|
.selectionChanged\
|
||||||
|
.connect(self.update)
|
||||||
|
|
||||||
|
self._table.dataChanged.connect(self.update)
|
||||||
|
self._table.layoutChanged.connect(self.update)
|
||||||
|
|
||||||
|
def index_selected(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0].row()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
if len(self._hs) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
logger.info("TODO: copy")
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
logger.info("TODO: paste")
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
|
||||||
|
def _set_checkbox_state(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
|
||||||
|
if row is None:
|
||||||
|
self._checkbox.setEnabled(False)
|
||||||
|
self._checkbox.setChecked(True)
|
||||||
|
else:
|
||||||
|
self._checkbox.setEnabled(True)
|
||||||
|
self._checkbox.setChecked(self._hs.basic_structure(row).enabled)
|
||||||
|
|
||||||
|
def _set_basic_structure_state(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
|
||||||
|
if row is not None:
|
||||||
|
self._table.enabled(
|
||||||
|
row,
|
||||||
|
self._checkbox.isChecked()
|
||||||
|
)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self._set_checkbox_state()
|
||||||
|
self._update_parameters_table()
|
||||||
|
|
||||||
|
def _update_parameters_table(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table_parameters.update_hs_index(row)
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# PlotAC.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tools import timer
|
||||||
|
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||||
|
from matplotlib import pyplot as plt
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
QCoreApplication
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class PlotAC(PamhyrPlot):
|
||||||
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
|
river=None, reach=None, profile=None,
|
||||||
|
parent=None):
|
||||||
|
super(PlotAC, self).__init__(
|
||||||
|
canvas=canvas,
|
||||||
|
trad=trad,
|
||||||
|
data=river,
|
||||||
|
toolbar=toolbar,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._current_reach = reach
|
||||||
|
self._current_profile = profile
|
||||||
|
|
||||||
|
@property
|
||||||
|
def river(self):
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
@river.setter
|
||||||
|
def river(self, river):
|
||||||
|
self.data = river
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def draw(self, highlight=None):
|
||||||
|
self.canvas.axes.cla()
|
||||||
|
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
|
||||||
|
|
||||||
|
if self.data is None:
|
||||||
|
self.line_kp = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._current_reach is None:
|
||||||
|
self.line_kp = None
|
||||||
|
return
|
||||||
|
|
||||||
|
reach = self._current_reach
|
||||||
|
|
||||||
|
self.canvas.axes.set_xlabel(
|
||||||
|
_translate("MainWindow_reach", "X (m)"),
|
||||||
|
color='black', fontsize=11
|
||||||
|
)
|
||||||
|
self.canvas.axes.set_ylabel(
|
||||||
|
_translate("MainWindow_reach", "Elevation (m)"),
|
||||||
|
color='black', fontsize=11
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._current_profile is None:
|
||||||
|
self.line_kp = None
|
||||||
|
else:
|
||||||
|
profile = self._current_profile
|
||||||
|
x = profile.get_station()
|
||||||
|
z = profile.z()
|
||||||
|
|
||||||
|
self.line_kp, = self.canvas.axes.plot(
|
||||||
|
x, z,
|
||||||
|
linestyle="solid",
|
||||||
|
lw=1.8,
|
||||||
|
color='grey',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.canvas.axes.relim()
|
||||||
|
self.canvas.axes.autoscale_view()
|
||||||
|
|
||||||
|
self.canvas.figure.tight_layout()
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
if self.toolbar is not None:
|
||||||
|
self.toolbar.update()
|
||||||
|
|
||||||
|
def set_reach(self, reach):
|
||||||
|
self._current_reach = reach
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def set_profile(self, profile):
|
||||||
|
self._current_profile = profile
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self.line_kp is None:
|
||||||
|
self.draw()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._current_reach is None or self._current_profile is None:
|
||||||
|
self.clear()
|
||||||
|
return
|
||||||
|
|
||||||
|
profile = self._current_profile
|
||||||
|
x = profile.get_station()
|
||||||
|
z = profile.z()
|
||||||
|
|
||||||
|
self.line_kp.set_data(x, z)
|
||||||
|
|
||||||
|
self.canvas.axes.relim()
|
||||||
|
self.canvas.axes.autoscale_view()
|
||||||
|
|
||||||
|
self.canvas.figure.tight_layout()
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
if self.line_kp is not None:
|
||||||
|
self.line_kp.set_data([], [])
|
||||||
|
|
||||||
|
self.canvas.figure.tight_layout()
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
# PlotKPC.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tools import timer
|
||||||
|
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
QCoreApplication
|
||||||
|
)
|
||||||
|
|
||||||
|
from matplotlib.collections import LineCollection
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class PlotKPC(PamhyrPlot):
|
||||||
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
|
river=None, reach=None, profile=None,
|
||||||
|
parent=None):
|
||||||
|
super(PlotKPC, self).__init__(
|
||||||
|
canvas=canvas,
|
||||||
|
trad=trad,
|
||||||
|
data=river,
|
||||||
|
toolbar=toolbar,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._current_reach = reach
|
||||||
|
self._current_profile = profile
|
||||||
|
|
||||||
|
@property
|
||||||
|
def river(self):
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
@river.setter
|
||||||
|
def river(self, river):
|
||||||
|
self.data = river
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def draw(self, highlight=None):
|
||||||
|
self.canvas.axes.cla()
|
||||||
|
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
|
||||||
|
|
||||||
|
if self.data is None:
|
||||||
|
self.profile = None
|
||||||
|
self.line_kp_zmin_zmax = None
|
||||||
|
self.line_kp_zmin = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._current_reach is None:
|
||||||
|
self.profile = None
|
||||||
|
self.line_kp_zmin_zmax = None
|
||||||
|
self.line_kp_zmin = None
|
||||||
|
return
|
||||||
|
|
||||||
|
reach = self._current_reach
|
||||||
|
|
||||||
|
self.canvas.axes.set_ylabel(
|
||||||
|
_translate("MainWindow_reach", "Elevation (m)"),
|
||||||
|
color='black', fontsize=11
|
||||||
|
)
|
||||||
|
self.canvas.axes.set_xlabel(
|
||||||
|
_translate("MainWindow_reach", "KP (m)"),
|
||||||
|
color='black', fontsize=11
|
||||||
|
)
|
||||||
|
|
||||||
|
kp = reach.reach.get_kp()
|
||||||
|
z_min = reach.reach.get_z_min()
|
||||||
|
z_max = reach.reach.get_z_max()
|
||||||
|
|
||||||
|
self.line_kp_zmin, = self.canvas.axes.plot(
|
||||||
|
kp, z_min,
|
||||||
|
color='grey', lw=1.
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(kp) != 0:
|
||||||
|
self.line_kp_zmin_zmax = self.canvas.axes.vlines(
|
||||||
|
x=kp,
|
||||||
|
ymin=z_min, ymax=z_max,
|
||||||
|
color='b',
|
||||||
|
lw=1.
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._current_profile is None:
|
||||||
|
self.profile = None
|
||||||
|
else:
|
||||||
|
self.profile, = self.canvas.axes.plot(
|
||||||
|
[self._current_profile.kp, self._current_profile.kp],
|
||||||
|
[self._current_profile.z_min(), self._current_profile.z_max()],
|
||||||
|
color='red', lw=1.
|
||||||
|
)
|
||||||
|
|
||||||
|
self.canvas.axes.relim()
|
||||||
|
self.canvas.figure.tight_layout()
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
if self.toolbar is not None:
|
||||||
|
self.toolbar.update()
|
||||||
|
|
||||||
|
def set_reach(self, reach):
|
||||||
|
self._current_reach = reach
|
||||||
|
self._current_profile = None
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def set_profile(self, profile):
|
||||||
|
self._current_profile = profile
|
||||||
|
self.update_profil()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def update_profil(self):
|
||||||
|
reach = self._current_reach
|
||||||
|
kp = reach.reach.get_kp()
|
||||||
|
z_min = reach.reach.get_z_min()
|
||||||
|
z_max = reach.reach.get_z_max()
|
||||||
|
|
||||||
|
if self.profile is None:
|
||||||
|
self.draw()
|
||||||
|
else:
|
||||||
|
self.profile.set_data(
|
||||||
|
[self._current_profile.kp, self._current_profile.kp],
|
||||||
|
[self._current_profile.z_min(), self._current_profile.z_max()],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.canvas.axes.relim()
|
||||||
|
self.canvas.axes.autoscale_view()
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
if self.profile is not None:
|
||||||
|
self.profile.set_data([], [])
|
||||||
|
|
||||||
|
if self.line_kp_zmin_zmax is not None:
|
||||||
|
self.line_kp_zmin_zmax.remove()
|
||||||
|
self.line_kp_zmin_zmax = None
|
||||||
|
|
||||||
|
if self.line_kp_zmin is not None:
|
||||||
|
self.line_kp_zmin.set_data([], [])
|
||||||
|
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
|
||||||
|
def clear_profile(self):
|
||||||
|
if self.profile is not None:
|
||||||
|
self.profile.set_data([], [])
|
||||||
|
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.HydraulicStructures.UndoCommand import (
|
||||||
|
SetNameCommand, SetReachCommand, SetKpCommand,
|
||||||
|
SetEnabledCommand, AddCommand, DelCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, trad=None, parent=None, mode="reaches"):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._trad = trad
|
||||||
|
self._mode = mode
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
val = []
|
||||||
|
if self._mode == "kp":
|
||||||
|
reach = self._data.hydraulic_structures\
|
||||||
|
.get(index.row())\
|
||||||
|
.input_reach
|
||||||
|
if reach is not None:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda kp: str(kp), reach.reach.get_kp()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda n: n.name, self._data.edges()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.addItems(
|
||||||
|
[_translate("Hydraulic structure", "Not associated")] +
|
||||||
|
val
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._data._hydraulic_structures
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
return self._lst.get(row).name
|
||||||
|
elif self._headers[column] == "reach":
|
||||||
|
n = self._lst.get(row).input_reach
|
||||||
|
if n is None:
|
||||||
|
return _translate("Hydraulic structure", "Not associated")
|
||||||
|
return n.name
|
||||||
|
elif self._headers[column] == "kp":
|
||||||
|
n = self._lst.get(row).input_kp
|
||||||
|
if n is None:
|
||||||
|
return _translate("Hydraulic structure", "Not associated")
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
na = _translate("Hydraulic structure", "Not associated")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
self._undo.push(
|
||||||
|
SetNameCommand(
|
||||||
|
self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "reach":
|
||||||
|
if value == na:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
SetReachCommand(
|
||||||
|
self._lst, row, self._data.edge(value)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "kp":
|
||||||
|
if value == na:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
SetKpCommand(
|
||||||
|
self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._lst, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._lst, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def enabled(self, row, enabled, parent=QModelIndex()):
|
||||||
|
self._undo.push(
|
||||||
|
SetEnabledCommand(
|
||||||
|
self._lst, row, enabled
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTranslate import PamhyrTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class HydraulicStructuresTranslate(PamhyrTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(HydraulicStructuresTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"name": _translate("HydraulicStructures", "Name"),
|
||||||
|
"reach": _translate("HydraulicStructures", "Reach"),
|
||||||
|
"kp": _translate("HydraulicStructures", "Kp"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SetNameCommand(QUndoCommand):
|
||||||
|
def __init__(self, h_s_lst, index, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._h_s_lst = h_s_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._h_s_lst.get(self._index).name
|
||||||
|
self._new = str(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._h_s_lst.get(self._index).name = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._h_s_lst.get(self._index).name = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetReachCommand(QUndoCommand):
|
||||||
|
def __init__(self, h_s_lst, index, reach):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._h_s_lst = h_s_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._h_s_lst.get(self._index).input_reach
|
||||||
|
self._new = reach
|
||||||
|
self._old_kp = self._h_s_lst.get(self._index).input_kp
|
||||||
|
self._new_kp = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
i = self._h_s_lst.get(self._index)
|
||||||
|
i.input_reach = self._old
|
||||||
|
i.input_kp = self._old_kp
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
i = self._h_s_lst.get(self._index)
|
||||||
|
i.input_reach = self._new
|
||||||
|
i.input_kp = self._new_kp
|
||||||
|
|
||||||
|
|
||||||
|
class SetKpCommand(QUndoCommand):
|
||||||
|
def __init__(self, h_s_lst, index, kp):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._h_s_lst = h_s_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._h_s_lst.get(self._index).input_kp
|
||||||
|
self._new = kp
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._h_s_lst.get(self._index).input_kp = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._h_s_lst.get(self._index).input_kp = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetEnabledCommand(QUndoCommand):
|
||||||
|
def __init__(self, h_s_lst, index, enabled):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._h_s_lst = h_s_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = not enabled
|
||||||
|
self._new = enabled
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._h_s_lst.get(self._index).enabled = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._h_s_lst.get(self._index).enabled = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, h_s_lst, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._h_s_lst = h_s_lst
|
||||||
|
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._h_s_lst.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._h_s_lst.new(self._h_s_lst, self._index)
|
||||||
|
else:
|
||||||
|
self._h_s_lst.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, h_s_lst, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._h_s_lst = h_s_lst
|
||||||
|
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._h_s = []
|
||||||
|
for row in rows:
|
||||||
|
self._h_s.append((row, self._h_s_lst.get(row)))
|
||||||
|
self._h_s.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._h_s:
|
||||||
|
self._h_s_lst.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._h_s_lst.delete_i(self._rows)
|
||||||
|
|
||||||
|
|
||||||
|
class PasteCommand(QUndoCommand):
|
||||||
|
def __init__(self, h_s_lst, row, h_s):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._h_s_lst = h_s_lst
|
||||||
|
|
||||||
|
self._row = row
|
||||||
|
self._h_s = deepcopy(h_s)
|
||||||
|
self._h_s.reverse()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._h_s_lst.delete_i(
|
||||||
|
self._tab,
|
||||||
|
range(self._row, self._row + len(self._h_s))
|
||||||
|
)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for r in self._h_s:
|
||||||
|
self._h_s_lst.insert(self._row, r)
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
|
pyqtSlot, pyqtSignal, QItemSelectionModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QHeaderView, QDoubleSpinBox, QVBoxLayout, QCheckBox
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
|
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||||
|
|
||||||
|
from View.HydraulicStructures.PlotAC import PlotAC
|
||||||
|
from View.HydraulicStructures.PlotKPC import PlotKPC
|
||||||
|
|
||||||
|
from View.HydraulicStructures.Table import (
|
||||||
|
TableModel, ComboBoxDelegate
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Network.GraphWidget import GraphWidget
|
||||||
|
from View.HydraulicStructures.Translate import HydraulicStructuresTranslate
|
||||||
|
|
||||||
|
from View.HydraulicStructures.BasicHydraulicStructures.Window import (
|
||||||
|
BasicHydraulicStructuresWindow
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class HydraulicStructuresWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "HydraulicStructures"
|
||||||
|
_pamhyr_name = "Hydraulic Structures"
|
||||||
|
|
||||||
|
def __init__(self, study=None, config=None, parent=None):
|
||||||
|
name = self._pamhyr_name + " - " + study.name
|
||||||
|
|
||||||
|
super(HydraulicStructuresWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=HydraulicStructuresTranslate(),
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hs_lst = self._study.river._hydraulic_structures
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_checkbox()
|
||||||
|
self.setup_plots()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self._table = None
|
||||||
|
|
||||||
|
self._delegate_reach = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
parent=self,
|
||||||
|
mode="reaches"
|
||||||
|
)
|
||||||
|
self._delegate_kp = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
parent=self,
|
||||||
|
mode="kp"
|
||||||
|
)
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["name", "reach", "kp"],
|
||||||
|
delegates={
|
||||||
|
"reach": self._delegate_reach,
|
||||||
|
"kp": self._delegate_kp,
|
||||||
|
},
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
)
|
||||||
|
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(0, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
def setup_checkbox(self):
|
||||||
|
self._checkbox = self.find(QCheckBox, f"checkBox")
|
||||||
|
self._set_checkbox_state()
|
||||||
|
|
||||||
|
def setup_plots(self):
|
||||||
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas.setObjectName("canvas")
|
||||||
|
self.toolbar = PamhyrPlotToolbar(
|
||||||
|
self.canvas, self
|
||||||
|
)
|
||||||
|
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||||
|
self.plot_layout.addWidget(self.toolbar)
|
||||||
|
self.plot_layout.addWidget(self.canvas)
|
||||||
|
|
||||||
|
self.plot_kpc = PlotKPC(
|
||||||
|
canvas=self.canvas,
|
||||||
|
river=self._study.river,
|
||||||
|
reach=None,
|
||||||
|
profile=None,
|
||||||
|
toolbar=self.toolbar
|
||||||
|
)
|
||||||
|
self.plot_kpc.draw()
|
||||||
|
|
||||||
|
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas_2.setObjectName("canvas_2")
|
||||||
|
self.toolbar_2 = PamhyrPlotToolbar(
|
||||||
|
self.canvas_2, self
|
||||||
|
)
|
||||||
|
self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2")
|
||||||
|
self.plot_layout_2.addWidget(self.toolbar_2)
|
||||||
|
self.plot_layout_2.addWidget(self.canvas_2)
|
||||||
|
|
||||||
|
self.plot_ac = PlotAC(
|
||||||
|
canvas=self.canvas_2,
|
||||||
|
river=self._study.river,
|
||||||
|
reach=None,
|
||||||
|
profile=None,
|
||||||
|
toolbar=self.toolbar_2
|
||||||
|
)
|
||||||
|
self.plot_ac.draw()
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_delete").triggered.connect(self.delete)
|
||||||
|
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
||||||
|
self._checkbox.clicked.connect(self._set_structure_state)
|
||||||
|
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
table.selectionModel()\
|
||||||
|
.selectionChanged\
|
||||||
|
.connect(self.update)
|
||||||
|
|
||||||
|
self._table.dataChanged.connect(self.update)
|
||||||
|
self._table.layoutChanged.connect(self.update)
|
||||||
|
|
||||||
|
def index_selected(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0].row()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._hs_lst) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
logger.info("TODO: copy")
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
logger.info("TODO: paste")
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
|
||||||
|
def edit(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
for row in rows:
|
||||||
|
data = self._hs_lst.get(row)
|
||||||
|
print(row)
|
||||||
|
print(data)
|
||||||
|
if self.sub_window_exists(
|
||||||
|
BasicHydraulicStructuresWindow,
|
||||||
|
data=[self._study, None, data]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
win = BasicHydraulicStructuresWindow(
|
||||||
|
data=data,
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
win.show()
|
||||||
|
|
||||||
|
def _set_checkbox_state(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
if row is None:
|
||||||
|
self._checkbox.setEnabled(False)
|
||||||
|
self._checkbox.setChecked(True)
|
||||||
|
else:
|
||||||
|
self._checkbox.setEnabled(True)
|
||||||
|
self._checkbox.setChecked(self._hs_lst.get(row).enabled)
|
||||||
|
|
||||||
|
def _set_structure_state(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
if row is not None:
|
||||||
|
self._table.enabled(
|
||||||
|
row,
|
||||||
|
self._checkbox.isChecked()
|
||||||
|
)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self._set_checkbox_state()
|
||||||
|
self._update_clear_plot()
|
||||||
|
|
||||||
|
def _update_clear_plot(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
if len(rows) == 0 or len(self._hs_lst) == 0:
|
||||||
|
self._update_clear_all()
|
||||||
|
return
|
||||||
|
|
||||||
|
reach = self._hs_lst.get(rows[0]).input_reach
|
||||||
|
if reach is not None:
|
||||||
|
self.plot_kpc.set_reach(reach)
|
||||||
|
self.plot_ac.set_reach(reach)
|
||||||
|
|
||||||
|
profile_kp = self._hs_lst.get(rows[0]).input_kp
|
||||||
|
if profile_kp is not None:
|
||||||
|
profiles = reach.reach\
|
||||||
|
.get_profiles_from_kp(
|
||||||
|
float(profile_kp)
|
||||||
|
)
|
||||||
|
|
||||||
|
if profiles is not None:
|
||||||
|
profile = profiles[0]
|
||||||
|
|
||||||
|
self.plot_kpc.set_profile(profile)
|
||||||
|
self.plot_ac.set_profile(profile)
|
||||||
|
else:
|
||||||
|
self._update_clear_profile()
|
||||||
|
else:
|
||||||
|
self._update_clear_profile()
|
||||||
|
else:
|
||||||
|
self._update_clear_all()
|
||||||
|
|
||||||
|
def _update_clear_all(self):
|
||||||
|
self.plot_kpc.clear()
|
||||||
|
self.plot_ac.clear()
|
||||||
|
|
||||||
|
def _update_clear_profile(self):
|
||||||
|
self.plot_ac.clear()
|
||||||
|
self.plot_kpc.clear_profile()
|
||||||
@@ -101,7 +101,7 @@ class EditLateralContributionWindow(PamhyrWindow):
|
|||||||
table_headers=headers,
|
table_headers=headers,
|
||||||
editable_headers=self._data.header,
|
editable_headers=self._data.header,
|
||||||
delegates={
|
delegates={
|
||||||
#"time": self._delegate_time,
|
# "time": self._delegate_time,
|
||||||
},
|
},
|
||||||
data=self._data,
|
data=self._data,
|
||||||
undo=self._undo_stack,
|
undo=self._undo_stack,
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class LateralContributionWindow(PamhyrWindow):
|
|||||||
self._table[t] = TableModel(
|
self._table[t] = TableModel(
|
||||||
table_view=table,
|
table_view=table,
|
||||||
table_headers=self._trad.get_dict("table_headers"),
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
editable_headers=True,
|
editable_headers=self._trad.get_dict("table_headers"),
|
||||||
delegates={
|
delegates={
|
||||||
"type": self._delegate_type,
|
"type": self._delegate_type,
|
||||||
"edge": self._delegate_edge,
|
"edge": self._delegate_edge,
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ from PyQt5.QtGui import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
QTranslator, QEvent, QUrl,
|
Qt, QTranslator, QEvent, QUrl,
|
||||||
)
|
)
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QMainWindow, QApplication, QAction,
|
QMainWindow, QApplication, QAction,
|
||||||
QFileDialog, QShortcut, QMenu, QToolBar,
|
QFileDialog, QShortcut, QMenu, QToolBar,
|
||||||
QMessageBox,
|
QMessageBox, QProgressDialog,
|
||||||
)
|
)
|
||||||
from PyQt5.uic import loadUi
|
from PyQt5.uic import loadUi
|
||||||
|
|
||||||
@@ -48,6 +48,7 @@ from View.Network.Window import NetworkWindow
|
|||||||
from View.Geometry.Window import GeometryWindow
|
from View.Geometry.Window import GeometryWindow
|
||||||
from View.BoundaryCondition.Window import BoundaryConditionWindow
|
from View.BoundaryCondition.Window import BoundaryConditionWindow
|
||||||
from View.Reservoir.Window import ReservoirWindow
|
from View.Reservoir.Window import ReservoirWindow
|
||||||
|
from View.HydraulicStructures.Window import HydraulicStructuresWindow
|
||||||
from View.LateralContribution.Window import LateralContributionWindow
|
from View.LateralContribution.Window import LateralContributionWindow
|
||||||
from View.InitialConditions.Window import InitialConditionsWindow
|
from View.InitialConditions.Window import InitialConditionsWindow
|
||||||
from View.Stricklers.Window import StricklersWindow
|
from View.Stricklers.Window import StricklersWindow
|
||||||
@@ -102,7 +103,8 @@ define_model_action = [
|
|||||||
"action_menu_boundary_conditions", "action_menu_initial_conditions",
|
"action_menu_boundary_conditions", "action_menu_initial_conditions",
|
||||||
"action_menu_edit_friction", "action_menu_edit_lateral_contribution",
|
"action_menu_edit_friction", "action_menu_edit_lateral_contribution",
|
||||||
"action_menu_run_solver", "action_menu_sediment_layers",
|
"action_menu_run_solver", "action_menu_sediment_layers",
|
||||||
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs"
|
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs",
|
||||||
|
"action_menu_edit_hydraulic_structures"
|
||||||
]
|
]
|
||||||
|
|
||||||
action = (
|
action = (
|
||||||
@@ -193,6 +195,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
"action_menu_edit_geometry": self.open_geometry,
|
"action_menu_edit_geometry": self.open_geometry,
|
||||||
"action_menu_boundary_conditions": self.open_boundary_cond,
|
"action_menu_boundary_conditions": self.open_boundary_cond,
|
||||||
"action_menu_edit_reservoirs": self.open_reservoir,
|
"action_menu_edit_reservoirs": self.open_reservoir,
|
||||||
|
"action_menu_edit_hydraulic_structures":
|
||||||
|
self.open_hydraulic_structures,
|
||||||
"action_menu_initial_conditions": self.open_initial_conditions,
|
"action_menu_initial_conditions": self.open_initial_conditions,
|
||||||
"action_menu_edit_friction": self.open_frictions,
|
"action_menu_edit_friction": self.open_frictions,
|
||||||
"action_menu_edit_lateral_contribution": self.open_lateral_contrib,
|
"action_menu_edit_lateral_contribution": self.open_lateral_contrib,
|
||||||
@@ -279,8 +283,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
Nothing
|
Nothing
|
||||||
"""
|
"""
|
||||||
self.update_enable_action()
|
self.update_enable_action()
|
||||||
# Maximise window
|
# self.showMaximized()
|
||||||
#self.showMaximized()
|
|
||||||
|
|
||||||
def set_debug_lvl(self, debug=True):
|
def set_debug_lvl(self, debug=True):
|
||||||
if debug:
|
if debug:
|
||||||
@@ -403,8 +406,20 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
if self._study.is_saved:
|
if self._study.is_saved:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
sql_request_count = self._study.sql_save_request_count()
|
||||||
|
progress = QProgressDialog(
|
||||||
|
"Saving...", None,
|
||||||
|
0, sql_request_count,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
progress.setWindowModality(Qt.WindowModal)
|
||||||
|
progress.setValue(0)
|
||||||
|
|
||||||
logger.info("Save...")
|
logger.info("Save...")
|
||||||
self._study.save()
|
self._study.save(
|
||||||
|
progress=lambda: progress.setValue(progress.value() + 1)
|
||||||
|
)
|
||||||
|
logger.info("Done")
|
||||||
|
|
||||||
def save_as_study(self):
|
def save_as_study(self):
|
||||||
"""Save current study as new file
|
"""Save current study as new file
|
||||||
@@ -420,12 +435,25 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
"", "Pamhyr(*.pamhyr)"
|
"", "Pamhyr(*.pamhyr)"
|
||||||
)
|
)
|
||||||
|
|
||||||
if file_name[-4:] == ".pamhyr":
|
if file_name.rsplit(".", 1)[-1] == ".pamhyr":
|
||||||
self._study.filename = file_name
|
self._study.filename = file_name
|
||||||
else:
|
else:
|
||||||
self._study.filename = file_name + ".pamhyr"
|
self._study.filename = file_name + ".pamhyr"
|
||||||
|
|
||||||
self._study.save()
|
sql_request_count = self._study.sql_save_request_count()
|
||||||
|
progress = QProgressDialog(
|
||||||
|
"Saving...", None,
|
||||||
|
0, sql_request_count,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
progress.setWindowModality(Qt.WindowModal)
|
||||||
|
progress.setValue(0)
|
||||||
|
|
||||||
|
logger.info("Save...")
|
||||||
|
self._study.save(
|
||||||
|
progress=lambda: progress.setValue(progress.value() + 1)
|
||||||
|
)
|
||||||
|
logger.info("Done")
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# MSG AND DIALOG #
|
# MSG AND DIALOG #
|
||||||
@@ -628,6 +656,19 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
reservoir = ReservoirWindow(study=self._study, parent=self)
|
reservoir = ReservoirWindow(study=self._study, parent=self)
|
||||||
reservoir.show()
|
reservoir.show()
|
||||||
|
|
||||||
|
def open_hydraulic_structures(self):
|
||||||
|
if self.sub_window_exists(
|
||||||
|
HydraulicStructuresWindow,
|
||||||
|
data=[self._study, None]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
hydraulic_structures = HydraulicStructuresWindow(
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
hydraulic_structures.show()
|
||||||
|
|
||||||
def open_lateral_contrib(self):
|
def open_lateral_contrib(self):
|
||||||
if self.sub_window_exists(
|
if self.sub_window_exists(
|
||||||
LateralContributionWindow,
|
LateralContributionWindow,
|
||||||
@@ -635,7 +676,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
lateral = LateralContributionWindow(study=self._study, parent=self)
|
lateral = LateralContributionWindow(
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
lateral.show()
|
lateral.show()
|
||||||
|
|
||||||
def open_stricklers(self):
|
def open_stricklers(self):
|
||||||
|
|||||||
@@ -178,12 +178,15 @@ class EdgeItem(QGraphicsItem):
|
|||||||
if self.graph.selected_item() == self:
|
if self.graph.selected_item() == self:
|
||||||
color = Qt.red
|
color = Qt.red
|
||||||
elif self.graph.current_edge() == self:
|
elif self.graph.current_edge() == self:
|
||||||
color = Qt.black
|
color = Qt.blue
|
||||||
elif not self.graph.graph.is_enable_edge(self.edge):
|
elif not self.graph.graph.is_enable_edge(self.edge):
|
||||||
color = Qt.darkGray
|
color = Qt.darkGray
|
||||||
|
|
||||||
painter.setPen(QPen(color, 2, Qt.SolidLine, Qt.RoundCap,
|
painter.setPen(
|
||||||
Qt.RoundJoin))
|
QPen(
|
||||||
|
color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin
|
||||||
|
)
|
||||||
|
)
|
||||||
# Draw the line
|
# Draw the line
|
||||||
painter.drawLine(line)
|
painter.drawLine(line)
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class Plot(PamhyrPlot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Plot label
|
# Plot label
|
||||||
#header = self.data.header
|
# header = self.data.header
|
||||||
self.canvas.axes.set_xlabel(
|
self.canvas.axes.set_xlabel(
|
||||||
self._table_headers["z"], color='black', fontsize=10
|
self._table_headers["z"], color='black', fontsize=10
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -88,18 +88,13 @@ class EditReservoirWindow(PamhyrWindow):
|
|||||||
def setup_table(self):
|
def setup_table(self):
|
||||||
headers = {}
|
headers = {}
|
||||||
table_headers = self._trad.get_dict("table_headers")
|
table_headers = self._trad.get_dict("table_headers")
|
||||||
#for h in self._data.header:
|
|
||||||
#headers[h] = table_headers[h]
|
|
||||||
|
|
||||||
table = self.find(QTableView, "tableView")
|
table = self.find(QTableView, "tableView")
|
||||||
self._table = TableModel(
|
self._table = TableModel(
|
||||||
table_view=table,
|
table_view=table,
|
||||||
table_headers=table_headers,
|
table_headers=table_headers,
|
||||||
editable_headers=table_headers,
|
editable_headers=table_headers,
|
||||||
#editable_headers=self._data.header,
|
delegates={},
|
||||||
delegates={
|
|
||||||
#"time": self._delegate_time,
|
|
||||||
},
|
|
||||||
data=self._data,
|
data=self._data,
|
||||||
undo=self._undo_stack,
|
undo=self._undo_stack,
|
||||||
opt_data=self._study.time_system
|
opt_data=self._study.time_system
|
||||||
@@ -181,7 +176,7 @@ class EditReservoirWindow(PamhyrWindow):
|
|||||||
rows = self.index_selected_rows()
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
table = []
|
table = []
|
||||||
#table.append(self._data.header)
|
# table.append(self._data.header)
|
||||||
table.append(self._trad.get_dict("table_headers"))
|
table.append(self._trad.get_dict("table_headers"))
|
||||||
|
|
||||||
data = self._data.data
|
data = self._data.data
|
||||||
|
|||||||
@@ -56,9 +56,22 @@ class ComboBoxDelegate(QItemDelegate):
|
|||||||
def createEditor(self, parent, option, index):
|
def createEditor(self, parent, option, index):
|
||||||
self.editor = QComboBox(parent)
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
nodes = list(
|
||||||
|
map(
|
||||||
|
lambda n: n.name,
|
||||||
|
filter(
|
||||||
|
lambda n: not (
|
||||||
|
self._data.is_upstream_node(n)
|
||||||
|
or self._data.is_downstream_node(n)
|
||||||
|
),
|
||||||
|
self._data.nodes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.editor.addItems(
|
self.editor.addItems(
|
||||||
[_translate("Reservoir", "Not associated")] +
|
[_translate("Reservoir", "Not associated")] +
|
||||||
self._data.nodes_names()
|
nodes
|
||||||
)
|
)
|
||||||
|
|
||||||
self.editor.setCurrentText(index.data(Qt.DisplayRole))
|
self.editor.setCurrentText(index.data(Qt.DisplayRole))
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
from tools import timer
|
from tools import timer
|
||||||
from View.Tools.PamhyrPlot import PamhyrPlot
|
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||||
|
from matplotlib import pyplot as plt
|
||||||
|
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
QCoreApplication
|
QCoreApplication
|
||||||
@@ -89,17 +90,17 @@ class PlotAC(PamhyrPlot):
|
|||||||
# Water elevation
|
# Water elevation
|
||||||
water_z = profile.get_ts_key(self._current_timestamp, "Z")
|
water_z = profile.get_ts_key(self._current_timestamp, "Z")
|
||||||
|
|
||||||
self.canvas.axes.plot(
|
self.water, = self.canvas.axes.plot(
|
||||||
[min(x), max(x)], [water_z, water_z],
|
[min(x), max(x)], [water_z, water_z],
|
||||||
lw=1., color='b',
|
lw=1., color='b',
|
||||||
)
|
)
|
||||||
|
|
||||||
x_z = list(map(lambda _: water_z, x))
|
self.collection = self.canvas.axes.fill_between(
|
||||||
self.canvas.axes.fill_between(
|
|
||||||
x, z, water_z,
|
x, z, water_z,
|
||||||
where=z <= water_z,
|
where=z <= water_z,
|
||||||
color='blue', alpha=0.5, interpolate=True
|
color='skyblue', alpha=0.7, interpolate=True
|
||||||
)
|
)
|
||||||
|
self.liste_chemins = self.collection.get_paths()
|
||||||
|
|
||||||
self.canvas.figure.tight_layout()
|
self.canvas.figure.tight_layout()
|
||||||
self.canvas.figure.canvas.draw_idle()
|
self.canvas.figure.canvas.draw_idle()
|
||||||
@@ -109,15 +110,60 @@ class PlotAC(PamhyrPlot):
|
|||||||
def set_reach(self, reach_id):
|
def set_reach(self, reach_id):
|
||||||
self._current_reach_id = reach_id
|
self._current_reach_id = reach_id
|
||||||
self._current_profile_id = 0
|
self._current_profile_id = 0
|
||||||
self.draw()
|
self.update()
|
||||||
|
|
||||||
def set_profile(self, profile_id):
|
def set_profile(self, profile_id):
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
self.draw()
|
self.update()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
self.draw()
|
self.update_poly()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.draw()
|
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
x = profile.geometry.get_station()
|
||||||
|
z = profile.geometry.z()
|
||||||
|
|
||||||
|
self.line_kp.set_data(x, z)
|
||||||
|
|
||||||
|
self.canvas.axes.set_xlim(
|
||||||
|
left=min(x), right=max(x)
|
||||||
|
)
|
||||||
|
|
||||||
|
kp = reach.geometry.get_kp()
|
||||||
|
|
||||||
|
# Water elevation
|
||||||
|
water_z = profile.get_ts_key(self._current_timestamp, "Z")
|
||||||
|
|
||||||
|
self.water.set_data([min(x), max(x)], [water_z, water_z])
|
||||||
|
|
||||||
|
self.collection.remove()
|
||||||
|
self.collection = self.canvas.axes.fill_between(
|
||||||
|
x, z, water_z,
|
||||||
|
where=z <= water_z,
|
||||||
|
color='skyblue', alpha=0.7, interpolate=True
|
||||||
|
)
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
|
||||||
|
def update_poly(self):
|
||||||
|
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
x = profile.geometry.get_station()
|
||||||
|
z = profile.geometry.z()
|
||||||
|
|
||||||
|
# Water elevation
|
||||||
|
water_z = profile.get_ts_key(self._current_timestamp, "Z")
|
||||||
|
|
||||||
|
self.water.set_data([min(x), max(x)], [water_z, water_z])
|
||||||
|
|
||||||
|
self.collection.remove()
|
||||||
|
self.collection = self.canvas.axes.fill_between(
|
||||||
|
x, z, water_z,
|
||||||
|
where=z <= water_z,
|
||||||
|
color='skyblue', alpha=0.7, interpolate=True
|
||||||
|
)
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
@@ -89,36 +89,27 @@ class PlotH(PamhyrPlot):
|
|||||||
color='black', fontsize=10
|
color='black', fontsize=10
|
||||||
)
|
)
|
||||||
|
|
||||||
ts = list(self.results.get("timestamps"))
|
self.ts = list(self.results.get("timestamps"))
|
||||||
ts.sort()
|
self.ts.sort()
|
||||||
|
|
||||||
self.canvas.axes.set_xlim(
|
|
||||||
left=min(ts), right=max(ts)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Draw discharge for each timestamp
|
# Draw discharge for each timestamp
|
||||||
x = ts
|
x = self.ts
|
||||||
y = profile.get_key("Q")
|
y = profile.get_key("Q")
|
||||||
|
|
||||||
if len(ts) != len(x):
|
self._line, = self.canvas.axes.plot(
|
||||||
logger.warning(
|
x, y, lw=1.,
|
||||||
"Results as less Q data ({len(x)}) " +
|
color='r',
|
||||||
"than timestamps ({len(ts)}) " +
|
markersize=3, marker='+'
|
||||||
"for profile {self._current_profile_id}"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.canvas.axes.set_ylim(
|
|
||||||
[min(min(y), 0), max(y) + 10]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._line = [
|
self._current, = self.canvas.axes.plot(
|
||||||
self.canvas.axes.plot(
|
self._current_timestamp,
|
||||||
x, y, lw=1.,
|
y[self.ts.index(self._current_timestamp)],
|
||||||
color='r',
|
lw=1., color='b',
|
||||||
markersize=3, marker='+'
|
markersize=3, marker='+'
|
||||||
)
|
)
|
||||||
]
|
|
||||||
|
self.canvas.axes.relim()
|
||||||
|
|
||||||
# Custom time display
|
# Custom time display
|
||||||
nb = len(x)
|
nb = len(x)
|
||||||
@@ -169,15 +160,29 @@ class PlotH(PamhyrPlot):
|
|||||||
def set_reach(self, reach_id):
|
def set_reach(self, reach_id):
|
||||||
self._current_reach_id = reach_id
|
self._current_reach_id = reach_id
|
||||||
self._current_profile_id = 0
|
self._current_profile_id = 0
|
||||||
self.draw()
|
self.update()
|
||||||
|
|
||||||
def set_profile(self, profile_id):
|
def set_profile(self, profile_id):
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
self.draw()
|
self.update()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
self.draw()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.draw()
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
|
x = self.ts
|
||||||
|
y = profile.get_key("Q")
|
||||||
|
|
||||||
|
self._line.set_data(x, y)
|
||||||
|
|
||||||
|
self._current.set_data(
|
||||||
|
self._current_timestamp,
|
||||||
|
y[self.ts.index(self._current_timestamp)]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.canvas.axes.relim()
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
@@ -72,6 +72,7 @@ class PlotKPC(PamhyrPlot):
|
|||||||
|
|
||||||
kp = reach.geometry.get_kp()
|
kp = reach.geometry.get_kp()
|
||||||
z_min = reach.geometry.get_z_min()
|
z_min = reach.geometry.get_z_min()
|
||||||
|
z_max = reach.geometry.get_z_max()
|
||||||
|
|
||||||
self.canvas.axes.set_xlim(
|
self.canvas.axes.set_xlim(
|
||||||
left=min(kp), right=max(kp)
|
left=min(kp), right=max(kp)
|
||||||
@@ -100,9 +101,21 @@ class PlotKPC(PamhyrPlot):
|
|||||||
|
|
||||||
self.canvas.axes.fill_between(
|
self.canvas.axes.fill_between(
|
||||||
kp, z_min, water_z,
|
kp, z_min, water_z,
|
||||||
color='blue', alpha=0.5, interpolate=True
|
color='skyblue', alpha=0.7, interpolate=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.profile, = self.canvas.axes.plot(
|
||||||
|
[
|
||||||
|
kp[self._current_profile_id],
|
||||||
|
kp[self._current_profile_id]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
z_max[self._current_profile_id],
|
||||||
|
z_min[self._current_profile_id]
|
||||||
|
],
|
||||||
|
color='red', lw=1.
|
||||||
|
)
|
||||||
|
|
||||||
self.canvas.figure.tight_layout()
|
self.canvas.figure.tight_layout()
|
||||||
self.canvas.figure.canvas.draw_idle()
|
self.canvas.figure.canvas.draw_idle()
|
||||||
if self.toolbar is not None:
|
if self.toolbar is not None:
|
||||||
@@ -111,15 +124,32 @@ class PlotKPC(PamhyrPlot):
|
|||||||
def set_reach(self, reach_id):
|
def set_reach(self, reach_id):
|
||||||
self._current_reach_id = reach_id
|
self._current_reach_id = reach_id
|
||||||
self._current_profile_id = 0
|
self._current_profile_id = 0
|
||||||
self.draw()
|
self.update()
|
||||||
|
|
||||||
def set_profile(self, profile_id):
|
def set_profile(self, profile_id):
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
self.draw()
|
self.update_profil()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
self.draw()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
def update_profil(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
kp = reach.geometry.get_kp()
|
||||||
|
z_min = reach.geometry.get_z_min()
|
||||||
|
z_max = reach.geometry.get_z_max()
|
||||||
|
self.profile.set_data(
|
||||||
|
[
|
||||||
|
kp[self._current_profile_id],
|
||||||
|
kp[self._current_profile_id]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
z_max[self._current_profile_id],
|
||||||
|
z_min[self._current_profile_id]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
@@ -51,13 +51,13 @@ class PlotSedProfile(PamhyrPlot):
|
|||||||
profiles_sl = list(
|
profiles_sl = list(
|
||||||
map(
|
map(
|
||||||
lambda sl: sl[0],
|
lambda sl: sl[0],
|
||||||
profile.get_ts_key(self._current_timestamp, "sl")
|
profile.get_ts_key(self._current_timestamp, "sl")[0]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
profiles_sl_0 = list(
|
profiles_sl_0 = list(
|
||||||
map(
|
map(
|
||||||
lambda sl: sl[0],
|
lambda sl: sl[0],
|
||||||
profile.get_ts_key(0.0, "sl")
|
profile.get_ts_key(0.0, "sl")[0]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -124,14 +124,14 @@ class PlotSedReach(PamhyrPlot):
|
|||||||
profiles_sl_0 = list(
|
profiles_sl_0 = list(
|
||||||
map(
|
map(
|
||||||
# Get SL list for profile p at time 0 (initial data)
|
# Get SL list for profile p at time 0 (initial data)
|
||||||
lambda p: p.get_ts_key(0.0, "sl"),
|
lambda p: p.get_ts_key(0.0, "sl")[0],
|
||||||
reach.profiles
|
reach.profiles
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
profiles_sl = list(
|
profiles_sl = list(
|
||||||
map(
|
map(
|
||||||
# Get SL list for profile p at current time
|
# Get SL list for profile p at current time
|
||||||
lambda p: p.get_ts_key(self._current_timestamp, "sl"),
|
lambda p: p.get_ts_key(self._current_timestamp, "sl")[0],
|
||||||
reach.profiles
|
reach.profiles
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -163,7 +163,10 @@ class PlotSedReach(PamhyrPlot):
|
|||||||
z_sl = reduce(
|
z_sl = reduce(
|
||||||
lambda acc, v: acc + [
|
lambda acc, v: acc + [
|
||||||
list(
|
list(
|
||||||
map(lambda x, y: y - x, v, acc[-1])
|
map(
|
||||||
|
lambda x, y: y - x,
|
||||||
|
v, acc[-1]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
sl_0,
|
sl_0,
|
||||||
|
|||||||
@@ -129,53 +129,26 @@ class PlotXY(PamhyrPlot):
|
|||||||
profile.x(),
|
profile.x(),
|
||||||
profile.y(),
|
profile.y(),
|
||||||
lw=1., markersize=3,
|
lw=1., markersize=3,
|
||||||
marker='+', color="b"
|
color="r", marker='+'
|
||||||
)
|
)
|
||||||
self.plot_selected.set_visible(False)
|
self.plot_selected.set_visible(True)
|
||||||
|
|
||||||
# Display point under water
|
poly_x = [0]
|
||||||
poly_l_x = []
|
poly_y = [0]
|
||||||
poly_l_y = []
|
|
||||||
poly_r_x = []
|
|
||||||
poly_r_y = []
|
|
||||||
for profile in reach.profiles:
|
|
||||||
water_z = profile.get_ts_key(
|
|
||||||
self._current_timestamp, "Z"
|
|
||||||
)
|
|
||||||
pgeo = profile.geometry
|
|
||||||
|
|
||||||
x, y = reduce(
|
self.fill = self.canvas.axes.fill(
|
||||||
lambda acc, pts: (acc[0] + [pts.x], acc[1] + [pts.y]),
|
poly_x, poly_y,
|
||||||
filter(
|
color='skyblue',
|
||||||
lambda pts: pts.z < water_z,
|
alpha=0.7
|
||||||
pgeo.points
|
)
|
||||||
),
|
|
||||||
([], [])
|
|
||||||
)
|
|
||||||
|
|
||||||
poly_l_x.append(x[0])
|
# self.canvas.axes.autoscale_view(True, True, True)
|
||||||
poly_l_y.append(y[0])
|
# self.canvas.axes.autoscale()
|
||||||
poly_r_x.append(x[-1])
|
|
||||||
poly_r_y.append(y[-1])
|
|
||||||
|
|
||||||
self.canvas.axes.plot(
|
|
||||||
x, y, lw=1.,
|
|
||||||
color='b',
|
|
||||||
markersize=1,
|
|
||||||
marker='o'
|
|
||||||
)
|
|
||||||
|
|
||||||
poly_x = poly_l_x + list(reversed(poly_r_x)) + [poly_l_x[0]]
|
|
||||||
poly_y = poly_l_y + list(reversed(poly_r_y)) + [poly_l_y[0]]
|
|
||||||
|
|
||||||
self.canvas.axes.fill(poly_x, poly_y, color='blue', alpha=1)
|
|
||||||
|
|
||||||
self.canvas.axes.autoscale_view(True, True, True)
|
|
||||||
self.canvas.axes.autoscale()
|
|
||||||
self.canvas.figure.tight_layout()
|
self.canvas.figure.tight_layout()
|
||||||
self.canvas.figure.canvas.draw_idle()
|
self.canvas.figure.canvas.draw_idle()
|
||||||
if self.toolbar is not None:
|
if self.toolbar is not None:
|
||||||
self.toolbar.update()
|
self.toolbar.update()
|
||||||
|
self.update()
|
||||||
|
|
||||||
def set_reach(self, reach_id):
|
def set_reach(self, reach_id):
|
||||||
self._current_reach_id = reach_id
|
self._current_reach_id = reach_id
|
||||||
@@ -184,11 +157,64 @@ class PlotXY(PamhyrPlot):
|
|||||||
|
|
||||||
def set_profile(self, profile_id):
|
def set_profile(self, profile_id):
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
self.draw()
|
self.update_profile()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
self.draw()
|
self.update_poly()
|
||||||
|
|
||||||
|
def update_profile(self):
|
||||||
|
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
if self.display_current:
|
||||||
|
# Current profile
|
||||||
|
profile = reach.profile(self._current_profile_id).geometry
|
||||||
|
|
||||||
|
self.plot_selected.set_data(profile.x(), profile.y())
|
||||||
|
self.plot_selected.set_visible(True)
|
||||||
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
|
def update_poly(self):
|
||||||
|
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id).geometry
|
||||||
|
|
||||||
|
# Display water
|
||||||
|
poly_l_x = []
|
||||||
|
poly_l_y = []
|
||||||
|
poly_r_x = []
|
||||||
|
poly_r_y = []
|
||||||
|
for profile in reach.profiles:
|
||||||
|
water_z = profile.get_ts_key(
|
||||||
|
self._current_timestamp, "Z"
|
||||||
|
)
|
||||||
|
ptX = profile.get_ts_key(
|
||||||
|
self._current_timestamp, "ptX"
|
||||||
|
)
|
||||||
|
ptY = profile.get_ts_key(
|
||||||
|
self._current_timestamp, "ptY"
|
||||||
|
)
|
||||||
|
|
||||||
|
poly_l_x.append(ptX.x)
|
||||||
|
poly_l_y.append(ptX.y)
|
||||||
|
poly_r_x.append(ptY.x)
|
||||||
|
poly_r_y.append(ptY.y)
|
||||||
|
|
||||||
|
# self.canvas.axes.plot(
|
||||||
|
# x, y, lw=1.,
|
||||||
|
# color='b',
|
||||||
|
# markersize=1,
|
||||||
|
# marker='o'
|
||||||
|
# )
|
||||||
|
|
||||||
|
poly_x = poly_l_x + list(reversed(poly_r_x))
|
||||||
|
poly_y = poly_l_y + list(reversed(poly_r_y))
|
||||||
|
poly = []
|
||||||
|
for i in range(len(poly_x)):
|
||||||
|
poly.append([poly_x[i], poly_y[i]])
|
||||||
|
self.fill[0].set_xy(poly)
|
||||||
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.draw()
|
self.update_profile()
|
||||||
|
self.update_poly()
|
||||||
@@ -16,21 +16,22 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from View.Tools.PamhyrWindow import PamhyrWindow
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QKeySequence,
|
QKeySequence, QIcon, QPixmap,
|
||||||
)
|
)
|
||||||
|
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
Qt, QVariant, QAbstractTableModel,
|
Qt, QVariant, QAbstractTableModel,
|
||||||
QCoreApplication, QModelIndex, pyqtSlot,
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
QItemSelectionModel,
|
QItemSelectionModel, QTimer,
|
||||||
)
|
)
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
@@ -69,6 +70,14 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
_pamhyr_ui = "Results"
|
_pamhyr_ui = "Results"
|
||||||
_pamhyr_name = "Results"
|
_pamhyr_name = "Results"
|
||||||
|
|
||||||
|
def _path_file(self, filename):
|
||||||
|
return os.path.abspath(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
"..", "..", filename
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, study=None, config=None,
|
def __init__(self, study=None, config=None,
|
||||||
solver=None, results=None,
|
solver=None, results=None,
|
||||||
parent=None):
|
parent=None):
|
||||||
@@ -97,7 +106,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
self._additional_plot = {}
|
self._additional_plot = {}
|
||||||
|
|
||||||
self.setup_table()
|
self.setup_table()
|
||||||
self.setup_plot()
|
self.setup_plots()
|
||||||
self.setup_slider()
|
self.setup_slider()
|
||||||
self.setup_statusbar()
|
self.setup_statusbar()
|
||||||
self.setup_connections()
|
self.setup_connections()
|
||||||
@@ -124,7 +133,25 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
self._slider_time.setMaximum(len(self._timestamps) - 1)
|
self._slider_time.setMaximum(len(self._timestamps) - 1)
|
||||||
self._slider_time.setValue(len(self._timestamps) - 1)
|
self._slider_time.setValue(len(self._timestamps) - 1)
|
||||||
|
|
||||||
def setup_plot(self):
|
file_path = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
self._icon_start = QIcon()
|
||||||
|
self._icon_start.addPixmap(
|
||||||
|
QPixmap(f'{file_path}/../ui/ressources/media-playback-start.png')
|
||||||
|
)
|
||||||
|
self._icon_pause = QIcon()
|
||||||
|
self._icon_pause.addPixmap(
|
||||||
|
QPixmap(f'{file_path}/../ui/ressources/media-playback-pause.png')
|
||||||
|
)
|
||||||
|
self._button_play = self.find(QPushButton, f"playButton")
|
||||||
|
self._button_play.setIcon(self._icon_start)
|
||||||
|
self._button_back = self.find(QPushButton, f"backButton")
|
||||||
|
self._button_next = self.find(QPushButton, f"nextButton")
|
||||||
|
self._button_first = self.find(QPushButton, f"firstButton")
|
||||||
|
self._button_last = self.find(QPushButton, f"lastButton")
|
||||||
|
self._timer = QTimer(self)
|
||||||
|
|
||||||
|
def setup_plots(self):
|
||||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
self.canvas.setObjectName("canvas")
|
self.canvas.setObjectName("canvas")
|
||||||
self.toolbar = PamhyrPlotToolbar(
|
self.toolbar = PamhyrPlotToolbar(
|
||||||
@@ -140,7 +167,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
reach_id=0,
|
reach_id=0,
|
||||||
profile_id=0,
|
profile_id=0,
|
||||||
toolbar=self.toolbar,
|
toolbar=self.toolbar,
|
||||||
display_current=False
|
display_current=True
|
||||||
)
|
)
|
||||||
self.plot_xy.draw()
|
self.plot_xy.draw()
|
||||||
|
|
||||||
@@ -238,6 +265,10 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
)
|
)
|
||||||
self.plot_sed_profile.draw()
|
self.plot_sed_profile.draw()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
self._timer.stop()
|
||||||
|
super(ResultsWindow, self).closeEvent(event)
|
||||||
|
|
||||||
def _compute_status_label(self):
|
def _compute_status_label(self):
|
||||||
# Timestamp
|
# Timestamp
|
||||||
ts = self._timestamps[self._slider_time.value()]
|
ts = self._timestamps[self._slider_time.value()]
|
||||||
@@ -310,6 +341,13 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
self._slider_profile.valueChanged.connect(
|
self._slider_profile.valueChanged.connect(
|
||||||
self._set_current_profile_slider)
|
self._set_current_profile_slider)
|
||||||
self._slider_time.valueChanged.connect(self._set_current_timestamp)
|
self._slider_time.valueChanged.connect(self._set_current_timestamp)
|
||||||
|
self._button_play.setChecked(False)
|
||||||
|
self._button_play.clicked.connect(self._pause)
|
||||||
|
self._button_back.clicked.connect(self._back)
|
||||||
|
self._button_next.clicked.connect(self._next)
|
||||||
|
self._button_first.clicked.connect(self._first)
|
||||||
|
self._button_last.clicked.connect(self._last)
|
||||||
|
self._timer.timeout.connect(self._update_slider)
|
||||||
|
|
||||||
def update_table_selection_reach(self, ind):
|
def update_table_selection_reach(self, ind):
|
||||||
table = self.find(QTableView, f"tableView_reach")
|
table = self.find(QTableView, f"tableView_reach")
|
||||||
@@ -373,7 +411,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
self.plot_xy.set_timestamp(timestamp)
|
self.plot_xy.set_timestamp(timestamp)
|
||||||
self.plot_ac.set_timestamp(timestamp)
|
self.plot_ac.set_timestamp(timestamp)
|
||||||
self.plot_kpc.set_timestamp(timestamp)
|
self.plot_kpc.set_timestamp(timestamp)
|
||||||
# self.plot_h.set_timestamp(timestamp)
|
self.plot_h.set_timestamp(timestamp)
|
||||||
|
|
||||||
if self._study.river.has_sediment():
|
if self._study.river.has_sediment():
|
||||||
self.plot_sed_reach.set_timestamp(timestamp)
|
self.plot_sed_reach.set_timestamp(timestamp)
|
||||||
@@ -525,3 +563,38 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
|
|
||||||
def _redo(self):
|
def _redo(self):
|
||||||
self._table.redo()
|
self._table.redo()
|
||||||
|
|
||||||
|
# play / pause buttons
|
||||||
|
def _update_slider(self):
|
||||||
|
if self._slider_time.value() == self._slider_time.maximum():
|
||||||
|
self._slider_time.setValue(self._slider_time.minimum())
|
||||||
|
else:
|
||||||
|
self._slider_time.setValue(self._slider_time.value()+1)
|
||||||
|
|
||||||
|
def _next(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.value()+1)
|
||||||
|
|
||||||
|
def _back(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.value()-1)
|
||||||
|
|
||||||
|
def _first(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.minimum())
|
||||||
|
|
||||||
|
def _last(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.maximum())
|
||||||
|
|
||||||
|
def _pause(self):
|
||||||
|
if self._button_play.isChecked():
|
||||||
|
self._button_next.setEnabled(False)
|
||||||
|
self._button_back.setEnabled(False)
|
||||||
|
self._button_first.setEnabled(False)
|
||||||
|
self._button_last.setEnabled(False)
|
||||||
|
self._timer.start(100)
|
||||||
|
self._button_play.setIcon(self._icon_pause)
|
||||||
|
else:
|
||||||
|
self._timer.stop()
|
||||||
|
self._button_next.setEnabled(True)
|
||||||
|
self._button_back.setEnabled(True)
|
||||||
|
self._button_first.setEnabled(True)
|
||||||
|
self._button_last.setEnabled(True)
|
||||||
|
self._button_play.setIcon(self._icon_start)
|
||||||
@@ -21,7 +21,7 @@ import logging
|
|||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from tools import trace, timer
|
from tools import trace, timer, logger_exception
|
||||||
|
|
||||||
from View.Tools.PamhyrWindow import PamhyrDialog, PamhyrWindow
|
from View.Tools.PamhyrWindow import PamhyrDialog, PamhyrWindow
|
||||||
|
|
||||||
@@ -74,10 +74,16 @@ class SelectSolverWindow(PamhyrDialog):
|
|||||||
|
|
||||||
self.setup_combobox()
|
self.setup_combobox()
|
||||||
self.setup_connections()
|
self.setup_connections()
|
||||||
|
self.select_last_solver()
|
||||||
|
|
||||||
def setup_combobox(self):
|
def setup_combobox(self):
|
||||||
solvers = self._config.solvers
|
solvers = self._config.solvers
|
||||||
solvers_name = list(map(lambda s: s.name + f" - ({s._type})", solvers))
|
solvers_name = list(
|
||||||
|
map(
|
||||||
|
self._format_solver_name,
|
||||||
|
solvers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.combobox_add_items("comboBox", solvers_name)
|
self.combobox_add_items("comboBox", solvers_name)
|
||||||
|
|
||||||
@@ -86,6 +92,26 @@ class SelectSolverWindow(PamhyrDialog):
|
|||||||
self.find(QPushButton, "pushButton_cancel")\
|
self.find(QPushButton, "pushButton_cancel")\
|
||||||
.clicked.connect(self.reject)
|
.clicked.connect(self.reject)
|
||||||
|
|
||||||
|
def select_last_solver(self):
|
||||||
|
solvers = self._config.solvers
|
||||||
|
last = self._config.last_solver_name
|
||||||
|
|
||||||
|
solver = list(
|
||||||
|
filter(
|
||||||
|
lambda s: s.name == last,
|
||||||
|
solvers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(solver) != 0:
|
||||||
|
self.set_combobox_text(
|
||||||
|
"comboBox",
|
||||||
|
self._format_solver_name(solver[0])
|
||||||
|
)
|
||||||
|
|
||||||
|
def _format_solver_name(self, solver):
|
||||||
|
return f"{solver.name} - ({solver._type})"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def solver(self):
|
def solver(self):
|
||||||
return self._solver
|
return self._solver
|
||||||
@@ -94,6 +120,8 @@ class SelectSolverWindow(PamhyrDialog):
|
|||||||
solver_name = self.get_combobox_text("comboBox")
|
solver_name = self.get_combobox_text("comboBox")
|
||||||
solver_name = solver_name.rsplit(" - ", 1)[0]
|
solver_name = solver_name.rsplit(" - ", 1)[0]
|
||||||
|
|
||||||
|
self._config.update_last_solver_used(solver_name)
|
||||||
|
|
||||||
self._solver = next(
|
self._solver = next(
|
||||||
filter(
|
filter(
|
||||||
lambda s: s.name == solver_name,
|
lambda s: s.name == solver_name,
|
||||||
@@ -124,40 +152,11 @@ class SolverLogWindow(PamhyrWindow):
|
|||||||
self.setup_action()
|
self.setup_action()
|
||||||
self.setup_alarm()
|
self.setup_alarm()
|
||||||
self.setup_connections()
|
self.setup_connections()
|
||||||
|
self.setup_workdir()
|
||||||
|
self.setup_process()
|
||||||
|
|
||||||
self._workdir = ""
|
self.export()
|
||||||
if self._study.filename == "":
|
self.run()
|
||||||
self._workdir = tempfile.TemporaryDirectory()
|
|
||||||
else:
|
|
||||||
self._workdir = os.path.join(
|
|
||||||
os.path.dirname(self._study.filename),
|
|
||||||
"_PAMHYR_",
|
|
||||||
self._study.name.replace(" ", "_"),
|
|
||||||
self._solver.name.replace(" ", "_"),
|
|
||||||
)
|
|
||||||
os.makedirs(self._workdir, exist_ok=True)
|
|
||||||
|
|
||||||
self._alarm.start(500)
|
|
||||||
self._output = Queue()
|
|
||||||
self._process = self.new_process(parent)
|
|
||||||
|
|
||||||
self._log(f" *** Export study {self._solver.name}", color="blue")
|
|
||||||
self._solver.export(self._study, self._workdir, qlog=self._output)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
self._log(f" *** Run solver {self._solver.name}", color="blue")
|
|
||||||
self._solver.run(
|
|
||||||
study,
|
|
||||||
process=self._process,
|
|
||||||
output_queue=self._output
|
|
||||||
)
|
|
||||||
|
|
||||||
def new_process(self, parent):
|
|
||||||
new = QProcess(parent)
|
|
||||||
new.setWorkingDirectory(self._workdir)
|
|
||||||
new.setProcessChannelMode(QProcess.MergedChannels)
|
|
||||||
return new
|
|
||||||
|
|
||||||
def setup_action(self):
|
def setup_action(self):
|
||||||
self.find(QAction, "action_start").setEnabled(False)
|
self.find(QAction, "action_start").setEnabled(False)
|
||||||
@@ -182,30 +181,78 @@ class SolverLogWindow(PamhyrWindow):
|
|||||||
|
|
||||||
self._alarm.timeout.connect(self.update)
|
self._alarm.timeout.connect(self.update)
|
||||||
|
|
||||||
|
def setup_workdir(self):
|
||||||
|
self._workdir = ""
|
||||||
|
if self._study.filename == "":
|
||||||
|
self._workdir = tempfile.TemporaryDirectory()
|
||||||
|
else:
|
||||||
|
self._workdir = os.path.join(
|
||||||
|
os.path.dirname(self._study.filename),
|
||||||
|
"_PAMHYR_",
|
||||||
|
self._study.name.replace(" ", "_"),
|
||||||
|
self._solver.name.replace(" ", "_"),
|
||||||
|
)
|
||||||
|
os.makedirs(self._workdir, exist_ok=True)
|
||||||
|
|
||||||
|
def setup_process(self):
|
||||||
|
self._alarm.start(100)
|
||||||
|
self._output = Queue()
|
||||||
|
self._process = self.new_process(self._parent)
|
||||||
|
|
||||||
|
def new_process(self, parent):
|
||||||
|
new = QProcess(parent)
|
||||||
|
new.setWorkingDirectory(self._workdir)
|
||||||
|
new.setProcessChannelMode(QProcess.MergedChannels)
|
||||||
|
return new
|
||||||
|
|
||||||
|
def export(self):
|
||||||
|
self._log(f" *** Export study {self._solver.name}", color="blue")
|
||||||
|
self._solver.export(self._study, self._workdir, qlog=self._output)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
self._alarm.stop()
|
||||||
|
super(SolverLogWindow, self).closeEvent(event)
|
||||||
|
|
||||||
|
#######
|
||||||
|
# LOG #
|
||||||
|
#######
|
||||||
|
|
||||||
def _log(self, msg, color=None):
|
def _log(self, msg, color=None):
|
||||||
if type(msg) is str:
|
if type(msg) is str:
|
||||||
logger.info(f"solver: {msg}")
|
self._log_str(msg, color)
|
||||||
|
|
||||||
msg = msg.rsplit('\n')[0]
|
|
||||||
|
|
||||||
if color is not None:
|
|
||||||
msg = f"<font color=\"{color}\">" + msg + "</font>"
|
|
||||||
|
|
||||||
self.find(QTextEdit, "textEdit").append(msg)
|
|
||||||
elif type(msg) is int:
|
elif type(msg) is int:
|
||||||
logger.info(f"solver: Returns {msg}")
|
self._log_int(msg, color)
|
||||||
|
|
||||||
color = "blue" if msg == 0 else "red"
|
def _log_str(self, msg, color=None):
|
||||||
self.find(QTextEdit, "textEdit")\
|
logger.info(f"solver: {msg}")
|
||||||
.append(f"<font color=\"{color}\">" +
|
msg = msg.rsplit('\n')[0]
|
||||||
f" *** Finished with code {msg}" +
|
|
||||||
"</font>")
|
|
||||||
|
|
||||||
self.statusbar.showMessage(
|
if color is not None:
|
||||||
"Done" if msg == 0 else "Failed",
|
msg = f"<font color=\"{color}\">" + msg + "</font>"
|
||||||
3000
|
|
||||||
|
self.find(QTextEdit, "textEdit").append(msg)
|
||||||
|
|
||||||
|
def _log_int(self, int_code, color=None):
|
||||||
|
logger.info(f"solver: Returns {int_code}")
|
||||||
|
color = "blue" if int_code == 0 else "red"
|
||||||
|
|
||||||
|
self.find(QTextEdit, "textEdit")\
|
||||||
|
.append(
|
||||||
|
f"<font color=\"{color}\">" +
|
||||||
|
f" *** Finished with code {int_code}" +
|
||||||
|
"</font>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.statusbar.showMessage(
|
||||||
|
"Done" if int_code == 0 else "Failed",
|
||||||
|
3000
|
||||||
|
)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# UPDATE #
|
||||||
|
##########
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
if self._solver.is_stoped():
|
if self._solver.is_stoped():
|
||||||
self.find(QAction, "action_start").setEnabled(True)
|
self.find(QAction, "action_start").setEnabled(True)
|
||||||
@@ -216,25 +263,43 @@ class SolverLogWindow(PamhyrWindow):
|
|||||||
if self._solver.log_file() != "":
|
if self._solver.log_file() != "":
|
||||||
self.find(QAction, "action_log_file").setEnabled(True)
|
self.find(QAction, "action_log_file").setEnabled(True)
|
||||||
|
|
||||||
|
self._update_logs_all()
|
||||||
self._update_get_results()
|
self._update_get_results()
|
||||||
|
|
||||||
self._update_logs_all()
|
self._update_logs_all()
|
||||||
|
|
||||||
def _update_get_results(self):
|
def _update_get_results(self):
|
||||||
if self._results is None:
|
if self._results is None:
|
||||||
self._results = self._solver.results(
|
try:
|
||||||
self._study, self._workdir, qlog=self._output
|
self._results = self._solver.results(
|
||||||
)
|
self._study, self._workdir, qlog=self._output
|
||||||
self._parent.set_results(self._solver, self._results)
|
)
|
||||||
|
self._parent.set_results(self._solver, self._results)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to open results")
|
||||||
|
logger_exception(e)
|
||||||
|
|
||||||
def _update_logs_all(self):
|
def _update_logs_all(self):
|
||||||
while self._output.qsize() != 0:
|
while self._output.qsize() != 0:
|
||||||
s = self._output.get()
|
s = self._output.get()
|
||||||
|
|
||||||
if type(s) is str and "[ERROR]" in s:
|
if type(s) is str and "[ERROR]" in s:
|
||||||
self._log(s, color="red")
|
self._log(s, color="red")
|
||||||
else:
|
else:
|
||||||
self._log(s)
|
self._log(s)
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Process controle #
|
||||||
|
####################
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._log(f" *** Run solver {self._solver.name}", color="blue")
|
||||||
|
self._solver.run(
|
||||||
|
self._study,
|
||||||
|
process=self._process,
|
||||||
|
output_queue=self._output
|
||||||
|
)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if self._solver.is_stoped():
|
if self._solver.is_stoped():
|
||||||
self._log(f" *** Export study {self._solver.name}", color="blue")
|
self._log(f" *** Export study {self._solver.name}", color="blue")
|
||||||
@@ -276,6 +341,10 @@ class SolverLogWindow(PamhyrWindow):
|
|||||||
if self._solver.log_file() != "":
|
if self._solver.log_file() != "":
|
||||||
self.find(QAction, "action_log_file").setEnabled(True)
|
self.find(QAction, "action_log_file").setEnabled(True)
|
||||||
|
|
||||||
|
###########
|
||||||
|
# Results #
|
||||||
|
###########
|
||||||
|
|
||||||
def results(self):
|
def results(self):
|
||||||
if self._results is None:
|
if self._results is None:
|
||||||
self._results = self._solver.results(
|
self._results = self._solver.results(
|
||||||
@@ -284,6 +353,8 @@ class SolverLogWindow(PamhyrWindow):
|
|||||||
self._parent.set_results(self._solver, self._results)
|
self._parent.set_results(self._solver, self._results)
|
||||||
self._parent.open_solver_results(self._solver, self._results)
|
self._parent.open_solver_results(self._solver, self._results)
|
||||||
|
|
||||||
|
self._solver.has_results_loaded()
|
||||||
|
|
||||||
def log_file(self):
|
def log_file(self):
|
||||||
file_name = os.path.join(self._workdir, self._solver.log_file())
|
file_name = os.path.join(self._workdir, self._solver.log_file())
|
||||||
log = SolverLogFileWindow(
|
log = SolverLogFileWindow(
|
||||||
|
|||||||
@@ -129,4 +129,7 @@ class ParamTranslate(PamhyrTranslate):
|
|||||||
"mage_min_reach_volume_to_check":
|
"mage_min_reach_volume_to_check":
|
||||||
_translate("SolverParameters",
|
_translate("SolverParameters",
|
||||||
"Minimum reach volume to check"),
|
"Minimum reach volume to check"),
|
||||||
|
"mage_init_internal":
|
||||||
|
_translate("SolverParameters",
|
||||||
|
"Use Mage internal initialization (Y/N)"),
|
||||||
}
|
}
|
||||||
@@ -101,4 +101,5 @@ class NewStudyWindow(PamhyrDialog):
|
|||||||
self._study.use_date(date)
|
self._study.use_date(date)
|
||||||
else:
|
else:
|
||||||
self._study.use_time()
|
self._study.use_time()
|
||||||
|
|
||||||
self.done(True)
|
self.done(True)
|
||||||
@@ -81,7 +81,7 @@ class WindowToolKit(object):
|
|||||||
header = row.copy()
|
header = row.copy()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
values.append(list(filter(lambda s: s != '', row)))
|
values.append(row)
|
||||||
|
|
||||||
return header, values
|
return header, values
|
||||||
|
|
||||||
|
|||||||
@@ -55,11 +55,12 @@ class ListedSubWindow(object):
|
|||||||
logger.info(f"Close window: ({h}) {self.sub_win_cnt}")
|
logger.info(f"Close window: ({h}) {self.sub_win_cnt}")
|
||||||
|
|
||||||
def _sub_win_exists(self, h):
|
def _sub_win_exists(self, h):
|
||||||
return reduce(
|
res = reduce(
|
||||||
lambda acc, el: (acc or (h == (el[1].hash()))),
|
lambda acc, el: (acc or (h == (el[1].hash()))),
|
||||||
self.sub_win_list,
|
self.sub_win_list,
|
||||||
False
|
False
|
||||||
)
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
def sub_win_exists(self, h):
|
def sub_win_exists(self, h):
|
||||||
return self._sub_win_exists(h)
|
return self._sub_win_exists(h)
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ class PamhyrTableModel(QAbstractTableModel):
|
|||||||
trad=None,
|
trad=None,
|
||||||
data=None,
|
data=None,
|
||||||
undo=None,
|
undo=None,
|
||||||
opt_data=None):
|
opt_data=None,
|
||||||
|
parent=None):
|
||||||
super(PamhyrTableModel, self).__init__()
|
super(PamhyrTableModel, self).__init__()
|
||||||
|
|
||||||
self._table_view = table_view
|
self._table_view = table_view
|
||||||
@@ -93,6 +94,7 @@ class PamhyrTableModel(QAbstractTableModel):
|
|||||||
self._editable_headers = editable_headers
|
self._editable_headers = editable_headers
|
||||||
self._delegates = delegates
|
self._delegates = delegates
|
||||||
self._trad = trad
|
self._trad = trad
|
||||||
|
self._parent = parent
|
||||||
|
|
||||||
self._data = data
|
self._data = data
|
||||||
self._opt_data = opt_data
|
self._opt_data = opt_data
|
||||||
@@ -119,8 +121,7 @@ class PamhyrTableModel(QAbstractTableModel):
|
|||||||
|
|
||||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||||
|
|
||||||
if (self._editable_headers or
|
if self._headers[column] in self._editable_headers:
|
||||||
self._headers[column] in self._editable_headers):
|
|
||||||
options |= Qt.ItemIsEditable
|
options |= Qt.ItemIsEditable
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
|||||||
@@ -186,3 +186,8 @@ class PamhyrDialog(ASubWindow, ListedSubWindow, PamhyrWindowTools):
|
|||||||
self._hash_data.append(self._config)
|
self._hash_data.append(self._config)
|
||||||
|
|
||||||
self._set_title()
|
self._set_title()
|
||||||
|
|
||||||
|
def done(self, result):
|
||||||
|
if self.parent is not None:
|
||||||
|
self.parent.sub_win_del(self.hash())
|
||||||
|
super(PamhyrDialog, self).done(result)
|
||||||
@@ -98,7 +98,7 @@ class PamhyrPlotToolbar(NavigationToolbar2QT):
|
|||||||
|
|
||||||
icon_zoom = QtGui.QIcon()
|
icon_zoom = QtGui.QIcon()
|
||||||
icon_zoom.addPixmap(QtGui.QPixmap(
|
icon_zoom.addPixmap(QtGui.QPixmap(
|
||||||
os.path.abspath(f"{file_path}/../ui/ressources/zoom.png")
|
os.path.abspath(f"{file_path}/../../ui/ressources/zoom.png")
|
||||||
))
|
))
|
||||||
|
|
||||||
icons.append(("zoom", icon_zoom))
|
icons.append(("zoom", icon_zoom))
|
||||||
@@ -122,7 +122,7 @@ class PamhyrPlotToolbar(NavigationToolbar2QT):
|
|||||||
icon_btn_isometric_view.addPixmap(
|
icon_btn_isometric_view.addPixmap(
|
||||||
QtGui.QPixmap(
|
QtGui.QPixmap(
|
||||||
os.path.abspath(
|
os.path.abspath(
|
||||||
f"{file_path}/../ui/ressources/zoom_fit_11.png"
|
f"{file_path}/../../ui/ressources/zoom_fit_11.png"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -131,7 +131,7 @@ class PamhyrPlotToolbar(NavigationToolbar2QT):
|
|||||||
icon_btn_global_view.addPixmap(
|
icon_btn_global_view.addPixmap(
|
||||||
QtGui.QPixmap(
|
QtGui.QPixmap(
|
||||||
os.path.abspath(
|
os.path.abspath(
|
||||||
f"{file_path}/../ui/ressources/zoom_fit.png"
|
f"{file_path}/../../ui/ressources/zoom_fit.png"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Basic hydraulic structures</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QSplitter" name="splitter_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_3">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable / Disable basic hydraulic structure</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView_2"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>22</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
<widget class="QToolBar" name="toolBar">
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>toolBar</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="toolBarArea">
|
||||||
|
<enum>TopToolBarArea</enum>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="toolBarBreak">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<addaction name="action_add"/>
|
||||||
|
<addaction name="action_delete"/>
|
||||||
|
</widget>
|
||||||
|
<action name="action_add">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Add a new point</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_delete">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/gtk-remove.png</normaloff>ressources/gtk-remove.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Delete points</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_edit">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Edit selected hydraulic structure</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
<string>Add</string>
|
<string>Add</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Add a new boundary condition or lateral contribution</string>
|
<string>Add a new boundary condition or punctual contribution</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Ctrl+N</string>
|
<string>Ctrl+N</string>
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
<string>Edit</string>
|
<string>Edit</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Edit boundary condition or lateral contribution</string>
|
<string>Edit boundary condition or punctual contribution</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Ctrl+E</string>
|
<string>Ctrl+E</string>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<string>Add</string>
|
<string>Add</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Add a new point in boundary condition or lateral contribution</string>
|
<string>Add a new point in boundary condition or punctual contribution</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Ctrl+N</string>
|
<string>Ctrl+N</string>
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<string>Sort</string>
|
<string>Sort</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Sort boundary condition point</string>
|
<string>Sort boundary condition points</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -19,78 +19,80 @@
|
|||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<widget class="QSplitter" name="splitter_3">
|
||||||
<item>
|
<property name="orientation">
|
||||||
<widget class="QSplitter" name="splitter_3">
|
<enum>Qt::Horizontal</enum>
|
||||||
<property name="orientation">
|
</property>
|
||||||
<enum>Qt::Horizontal</enum>
|
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||||
</property>
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<widget class="QTableView" name="tableView"/>
|
|
||||||
<widget class="QSplitter" name="splitter_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QSplitter" name="splitter">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="verticalLayoutWidget">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="verticalLayoutWidget_2">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="verticalLayoutWidget_3">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<widget class="QTableView" name="tableView"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSlider" name="verticalSlider">
|
<widget class="QPushButton" name="pushButton_up">
|
||||||
<property name="orientation">
|
<property name="text">
|
||||||
<enum>Qt::Vertical</enum>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="invertedAppearance">
|
<property name="icon">
|
||||||
<bool>true</bool>
|
<iconset>
|
||||||
|
<normaloff>ressources/go-up2.png</normaloff>ressources/go-up2.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="invertedControls">
|
</widget>
|
||||||
<bool>true</bool>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="verticalSlider">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="invertedAppearance">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="invertedControls">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_down">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/go-down1.png</normaloff>ressources/go-down1.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="pushButton_up">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset>
|
|
||||||
<normaloff>ressources/go-up2.png</normaloff>ressources/go-up2.png</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="pushButton_down">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset>
|
|
||||||
<normaloff>ressources/go-down1.png</normaloff>ressources/go-down1.png</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</widget>
|
||||||
</layout>
|
<widget class="QSplitter" name="splitter_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_3">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Hydraulic structures</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QSplitter" name="splitter_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_3">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable / Disable hydraulic structure</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>22</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
<widget class="QToolBar" name="toolBar">
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>toolBar</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="toolBarArea">
|
||||||
|
<enum>TopToolBarArea</enum>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="toolBarBreak">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<addaction name="action_add"/>
|
||||||
|
<addaction name="action_delete"/>
|
||||||
|
<addaction name="action_edit"/>
|
||||||
|
</widget>
|
||||||
|
<action name="action_add">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Add a new point</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_delete">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/gtk-remove.png</normaloff>ressources/gtk-remove.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Delete points</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_edit">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Edit selected hydraulic structure</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -132,6 +132,7 @@
|
|||||||
<addaction name="action_menu_edit_friction"/>
|
<addaction name="action_menu_edit_friction"/>
|
||||||
<addaction name="action_menu_edit_lateral_contribution"/>
|
<addaction name="action_menu_edit_lateral_contribution"/>
|
||||||
<addaction name="action_menu_edit_reservoirs"/>
|
<addaction name="action_menu_edit_reservoirs"/>
|
||||||
|
<addaction name="action_menu_edit_hydraulic_structures"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_results">
|
<widget class="QMenu" name="menu_results">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@@ -490,7 +491,7 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="action_menu_boundary_conditions">
|
<action name="action_menu_boundary_conditions">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Boundary conditions and one-time contributions</string>
|
<string>Boundary conditions and punctual contributions</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font/>
|
<font/>
|
||||||
@@ -799,7 +800,7 @@
|
|||||||
<string>Boundary conditions</string>
|
<string>Boundary conditions</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Edit boundary conditions and one-time contributions</string>
|
<string>Edit boundary conditions and punctual contributions</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font/>
|
<font/>
|
||||||
@@ -934,6 +935,14 @@
|
|||||||
<string>Edit reservoirs</string>
|
<string>Edit reservoirs</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_menu_edit_hydraulic_structures">
|
||||||
|
<property name="text">
|
||||||
|
<string>Hydraulic structures</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Edit hydraulic structures</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>942</width>
|
<width>1280</width>
|
||||||
<height>655</height>
|
<height>720</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -17,6 +17,12 @@
|
|||||||
<locale language="English" country="Europe"/>
|
<locale language="English" country="Europe"/>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>874</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QSplitter" name="splitter_4">
|
<widget class="QSplitter" name="splitter_4">
|
||||||
@@ -28,7 +34,26 @@
|
|||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QTableView" name="tableView_reach"/>
|
<widget class="QTableView" name="tableView_reach"/>
|
||||||
<widget class="QTableView" name="tableView_profile"/>
|
<widget class="QWidget" name="layoutWidget">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView_profile"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="verticalSlider_profile">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="invertedAppearance">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="invertedControls">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="layoutWidget">
|
<widget class="QWidget" name="layoutWidget">
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
@@ -43,25 +68,29 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QSplitter" name="splitter_2">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<property name="orientation">
|
<item>
|
||||||
<enum>Qt::Vertical</enum>
|
<widget class="QSplitter" name="splitter_2">
|
||||||
</property>
|
<property name="orientation">
|
||||||
<widget class="QSplitter" name="splitter">
|
<enum>Qt::Vertical</enum>
|
||||||
<property name="orientation">
|
</property>
|
||||||
<enum>Qt::Horizontal</enum>
|
<widget class="QSplitter" name="splitter">
|
||||||
</property>
|
<property name="orientation">
|
||||||
<widget class="QWidget" name="verticalLayoutWidget">
|
<enum>Qt::Horizontal</enum>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
</property>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_3">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="verticalLayoutWidget_2">
|
</item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
</layout>
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="verticalLayoutWidget_3">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -103,25 +132,90 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QSlider" name="verticalSlider_profile">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="invertedAppearance">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="invertedControls">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QSlider" name="horizontalSlider_time">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<property name="orientation">
|
<item>
|
||||||
<enum>Qt::Horizontal</enum>
|
<widget class="QPushButton" name="firstButton">
|
||||||
</property>
|
<property name="text">
|
||||||
</widget>
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/media-skip-backward.png</normaloff>ressources/media-skip-backward.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="backButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/media-seek-backward.png</normaloff>ressources/media-seek-backward.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="playButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="mouseTracking">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="contextMenuPolicy">
|
||||||
|
<enum>Qt::DefaultContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/media-playback-start.png</normaloff>
|
||||||
|
<normalon>ressources/media-playback-pause.png</normalon>ressources/media-playback-start.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="nextButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/media-seek-forward.png</normaloff>ressources/media-seek-forward.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="lastButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>ressources/media-skip-forward.png</normaloff>ressources/media-skip-forward.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="horizontalSlider_time">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -134,7 +228,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>942</width>
|
<width>1280</width>
|
||||||
<height>22</height>
|
<height>22</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 217 B |
|
After Width: | Height: | Size: 472 B |
|
After Width: | Height: | Size: 539 B |
|
After Width: | Height: | Size: 610 B |
|
After Width: | Height: | Size: 557 B |
|
After Width: | Height: | Size: 558 B |
@@ -35,7 +35,7 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
class Config(SQL):
|
class Config(SQL):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._version = '0.0.3'
|
self._version = '0.0.4'
|
||||||
self.filename = Config.filename()
|
self.filename = Config.filename()
|
||||||
self.set_default_value()
|
self.set_default_value()
|
||||||
|
|
||||||
@@ -113,11 +113,17 @@ class Config(SQL):
|
|||||||
''
|
''
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if int(release) < 3:
|
if int(release) < 3:
|
||||||
self.execute(f"INSERT INTO data VALUES ('last_study', '')")
|
self.execute(f"INSERT INTO data VALUES ('last_study', '')")
|
||||||
self.execute(
|
self.execute(
|
||||||
f"INSERT INTO data VALUES ('close_correctly', 'True')")
|
f"INSERT INTO data VALUES ('close_correctly', 'True')")
|
||||||
|
|
||||||
|
if int(release) < 4:
|
||||||
|
self.execute(
|
||||||
|
f"INSERT INTO data VALUES ('last_solver_name', '')"
|
||||||
|
)
|
||||||
|
|
||||||
self.commit()
|
self.commit()
|
||||||
|
|
||||||
def _load_solver(self):
|
def _load_solver(self):
|
||||||
@@ -195,6 +201,10 @@ class Config(SQL):
|
|||||||
v = self.execute("SELECT value FROM data WHERE key='close_correctly'")
|
v = self.execute("SELECT value FROM data WHERE key='close_correctly'")
|
||||||
self.close_correctly = v[0] == "True"
|
self.close_correctly = v[0] == "True"
|
||||||
|
|
||||||
|
# Last Solver
|
||||||
|
v = self.execute("SELECT value FROM data WHERE key='last_solver_name'")
|
||||||
|
self.last_solver_name = v[0]
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
v = self.execute("SELECT value FROM data WHERE key='debug'")
|
v = self.execute("SELECT value FROM data WHERE key='debug'")
|
||||||
self.debug = v[0] == "True"
|
self.debug = v[0] == "True"
|
||||||
@@ -250,6 +260,7 @@ class Config(SQL):
|
|||||||
"last_study": self.last_study,
|
"last_study": self.last_study,
|
||||||
"close_correctly": self.close_correctly,
|
"close_correctly": self.close_correctly,
|
||||||
"debug": self.debug,
|
"debug": self.debug,
|
||||||
|
"last_solver_name": self.last_solver_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in data:
|
for key in data:
|
||||||
@@ -306,6 +317,9 @@ class Config(SQL):
|
|||||||
self.last_study = ""
|
self.last_study = ""
|
||||||
self.close_correctly = False
|
self.close_correctly = False
|
||||||
|
|
||||||
|
# Last Solver
|
||||||
|
self.last_solver_name = ""
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
self.debug = False
|
self.debug = False
|
||||||
|
|
||||||
@@ -332,6 +346,14 @@ class Config(SQL):
|
|||||||
)
|
)
|
||||||
self.commit()
|
self.commit()
|
||||||
|
|
||||||
|
def update_last_solver_used(self, solver_name):
|
||||||
|
self.last_solver_name = solver_name
|
||||||
|
self.execute(
|
||||||
|
"UPDATE data SET " +
|
||||||
|
f"value='{self._db_format(self.last_solver_name)}' " +
|
||||||
|
"WHERE key='last_solver_name'"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def filename(cls):
|
def filename(cls):
|
||||||
file = ""
|
file = ""
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ from Scripts.P3DST import Script3DST
|
|||||||
from Scripts.Hello import ScriptHello
|
from Scripts.Hello import ScriptHello
|
||||||
from Scripts.ListSolver import ScriptListSolver
|
from Scripts.ListSolver import ScriptListSolver
|
||||||
from Scripts.Run import ScriptExport, ScriptRun
|
from Scripts.Run import ScriptExport, ScriptRun
|
||||||
|
from Scripts.MageMesh import MageMesh
|
||||||
|
|
||||||
from init import legal_info, debug_info, setup_lang
|
from init import legal_info, debug_info, setup_lang
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ scripts = {
|
|||||||
"export": ScriptExport,
|
"export": ScriptExport,
|
||||||
"run": ScriptRun,
|
"run": ScriptRun,
|
||||||
"3DST": Script3DST,
|
"3DST": Script3DST,
|
||||||
|
"mesh": MageMesh,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ def logger_color_reset():
|
|||||||
return f"{Style.RESET_ALL}"
|
return f"{Style.RESET_ALL}"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def logger_exception(exception):
|
def logger_exception(exception):
|
||||||
logger.error(
|
logger.error(
|
||||||
f"[{Fore.RED}ERROR{Style.RESET_ALL}] " +
|
f"[{Fore.RED}ERROR{Style.RESET_ALL}] " +
|
||||||
|
|||||||