Merge branch 'terraz_dev' into adists_release

adists_release
Theophile Terraz 2024-11-07 16:29:19 +01:00
commit 1266884f93
33 changed files with 1193 additions and 349 deletions

3
.gitignore vendored
View File

@ -41,6 +41,9 @@ __old__
*-venv
mage8
adists
### END CUSTOM ###
# Created by https://www.toptal.com/developers/gitignore/api/python

View File

@ -28,20 +28,6 @@
#+OPTIONS: toc:t
#+LANGUAGE: UKenglish
#+NAME: attr_wrap
#+HEADER: :var width="\\textwidth"
#+HEADER: :var caption=""
#+HEADER: :var smallcaption=""
#+HEADER: :var name=""
#+HEADER: :var data=""
#+HEADER: :var float="nil"
#+BEGIN_SRC sh :results output :exports none
echo "#+CAPTION[$smallcaption]: $caption"
echo "#+NAME: $name"
echo "#+ATTR_LATEX: :width $width :float $float"
echo "$data"
#+END_SRC
#+BEGIN_abstract
This document is for the use of developers. It describes the project
architecture, the tools available to assist development and
@ -54,13 +40,13 @@ documentation, translations or code.
* Introduction
Pamhyr2 is free and open source software (FOSS) graphical user
{{{pamhyr2}}} is free and open source software (FOSS) graphical user
interface (GUI) for 1D hydro-sedimentary modelling of rivers developed
in Python (with version 3.8). It use PyQt at version 5 and matplotlib
in version 3.4.1 or later for the user insterface (see
{{{file(/requirements.txt)}}} for details). The architecture of
project code follow the Qt Model/View architecture [fn:qt-arch] (see
details in section [[Architecture]]). Pamhyr2 packages can be build
details in section [[Architecture]]). {{{pamhyr2}}} packages can be build
manually (see section [[Building packages]]), but there are automatically
build with the gitlab-ci (see the section [[Setup the CI
environment]]). Documentation files are written with org-mode[fn:org],
@ -75,7 +61,7 @@ https://doc.qt.io/qt-5/model-view-programming.html (last access
* Architecture
Pamhyr2's architecture is based on Qt Model/View, see Figure
{{{pamhyr2}}}'s architecture is based on Qt Model/View, see Figure
[[graph-architecture]]. It is made up of several different components: the
model (in blue), the graphical components (in red), the
actions/delegates (in green), the commands (in purple), the solvers
@ -178,7 +164,7 @@ https://doc.qt.io/qt-5/model-view-programming.html
** Model
The model is a set of Python classes. In Pamhyr2, this classes must
The model is a set of Python classes. In {{{pamhyr2}}}, this classes must
respect some constraint. Each model class must inherits
=Model.Tools.SQLSubModel= abstract class, except the =Model.Study=
class who inherits =Model.Tools.SQLModel= (see [[SQL]]).
@ -456,12 +442,12 @@ of Bar (Listing [[sql-bar]] and [[sql-foo]]).
bar._sql_save(execute, data=data)
#+end_src
Let see the results database scheme for Pamhyr2 at version v0.0.7 in
Let see the results database scheme for {{{pamhyr2}}} at version v0.0.7 in
Figure [[sql_schema]].
#+NAME: sql_schema
#+ATTR_LATEX: :width 16cm
#+CAPTION: SQLite database scheme at Pamhyr2 version v0.0.7 (generate with [[https://gitlab.com/Screwtapello/sqlite-schema-diagram]])
#+CAPTION: SQLite database scheme at {{{pamhyr2}}} version v0.0.7 (generate with [[https://gitlab.com/Screwtapello/sqlite-schema-diagram]])
[[./images/schema_v0.0.7.png]]
@ -486,7 +472,7 @@ PamhyrModelList but use a dictionary instead of list.
** View
Pamhyr2 use Qt as graphical user interface library with the
{{{pamhyr2}}} use Qt as graphical user interface library with the
application "Qt designer" for windows or widget creation (see [[UI file]])
and "Qt linguist" for interface translate (see [[Translate]]). In
addition, we use matplotlib as ploting library (see [[Plot]]).
@ -501,11 +487,11 @@ componant translate, and possible other files or sub-directories.
*** UI file
We define as possible all Pamhyr2 windows and custom widgets with "Qt
designer". This application generate UI file who describes interface
organisation with table, layout, button, etc. This method is faster
than hand made windows and widget creation, and saves us some purely
descriptive code. The UI files are saved into =src/View/ui= for
We define as possible all {{{pamhyr2}}} windows and custom widgets
with "Qt designer". This application generate UI file who describes
interface organisation with table, layout, button, etc. This method is
faster than hand made windows and widget creation, and saves us some
purely descriptive code. The UI files are saved into =src/View/ui= for
window, and =/src/View/ui/Widgets= for custom widget.
*** Translate
@ -533,10 +519,11 @@ window, and =/src/View/ui/Widgets= for custom widget.
*** Window
The abstract class PamhyrWindow and PamhyrDialog are used for most of
Pamhyr2 window. These class allow to create an window for Pamhyr2 GUI
and implemente some useful methods. The super class method difine some
generic value from optional parameters, for examples:
The abstract class =PamhyrWindow= and =PamhyrDialog= are used for most
of {{{pamhyr2}}} window. These class allow to create an window for
{{{pamhyr2}}} GUI and implemente some useful methods. The super class
method difine some generic value from optional parameters, for
examples:
- =self._study=: The study giving in constructor parameters =study=
(typically a =Model.Study= class object)
- =self._config=: The configuration giving in constructor parameters
@ -546,7 +533,7 @@ generic value from optional parameters, for examples:
object)
#+NAME: window
#+CAPTION: Example of Pamhyr2 window
#+CAPTION: Example of {{{pamhyr2}}} window
#+begin_src python :python python3 :results output :noweb yes
from View.Tools.PamhyrWindow import PamhyrWindow
from View.My.Translate import MyTranslate
@ -593,7 +580,7 @@ window componants or connections.
*** Table
An abstract class PamhyrTableModel is available to define a simple
An abstract class =PamhyrTableModel= is available to define a simple
QAbstractTableModel shortly. In simple cases, there are only =data=
and =setData= methode to implement, but the constructor needs more
information than a classic QAbstractTableModel class.
@ -654,7 +641,7 @@ class AddNodeCommand(QUndoCommand):
All undo command must be push into a =QUndoStack= (see Listing
[[undo-cmd-push]]) to perform the action and allow user undo and redo this
action. In PamhyrWindow (and PamhyrDialog) the undo stack is
action. In =PamhyrWindow= (and =PamhyrDialog=) the undo stack is
automatically create if the option ="undo"= is activate at window
creation, this stack is accessible at =self._undo_stack=.
@ -672,7 +659,7 @@ creation, this stack is accessible at =self._undo_stack=.
*** Plot
To define a new plot you can create a class who inherit to
PamhyrPlot. The creator need at leaste five argument:
=PamhyrPlot=. The creator need at leaste five argument:
- A =canvas= of type =MplCanvas=
- A (optional) =trad= of type =PamhyrTranslate=
- A =data= used in =draw= and =update= to create and update the plot
@ -719,8 +706,8 @@ data has changed.
** Solver
The Pamhyr2 architecture allow to define multiple solver. A solver is
define by a:
The {{{pamhyr2}}} architecture allow to define multiple solver. A
solver is define by a:
- type
- name
- description,
@ -736,11 +723,11 @@ different solver type, this solver type implement the code for export
study to solver input format, and read the solver output to study
results. There exists a generic solver with a generic input and output
format, the type could be use to use a solver not implemented in
Pamhyr2, but this solver must can read/write input and output generic
format or use external script. There is possible to define different
solver with the same type, for example two differents version of the
same solver. Finaly, with input and output formater is possible to
execute a code on distant computer, for example, over ssh.
{{{pamhyr2}}}, but this solver must can read/write input and output
generic format or use external script. There is possible to define
different solver with the same type, for example two differents
version of the same solver. Finaly, with input and output formater is
possible to execute a code on distant computer, for example, over ssh.
#+name: graph-multi-solver
#+header: :results drawer
@ -857,8 +844,8 @@ solver and get results:
- (2.1) The solver read the input file(s)
- (2.2) The solver compute results and write it to solver output
file(s)
- (3) Pamhyr2 create a =Results= object
- (3.1) The Pamhyr2 solver class read solver output file(s) and
- (3) {{{pamhyr2}}} create a =Results= object
- (3.1) The {{{pamhyr2}}} solver class read solver output file(s) and
complete Results with readed data
#+name: graph-pipeline
@ -976,7 +963,7 @@ the temporal order of action is prensented in Figure
}
#+end_src
To implement a Solver in Pamhyr2, there exists a abstract class
To implement a Solver in {{{pamhyr2}}}, there exists a abstract class
=Solver.AbstractSolver=. A class who herits this class, must implement
different methods:
- =export=: Export the study to solver input file(s)
@ -987,7 +974,7 @@ different methods:
** Unit tests
A very small part of Pamhyr2 has unit test. This part is limited to the Model.
A very small part of {{{pamhyr2}}} has unit test. This part is limited to the Model.
#+begin_src shell
python3 -m venv test
@ -1000,9 +987,9 @@ A very small part of Pamhyr2 has unit test. This part is limited to the Model.
** The debug mode
To activate an deactivate the Pamhyr2 debug mode you can open the
configuration window and type "Ctrl+G" or run Pamhyr2 with command
line:
To activate an deactivate the {{{pamhyr2}}} debug mode you can open
the configuration window and type "Ctrl+G" or run {{{pamhyr2}}} with
command line:
#+begin_src shell
./Pamhyr2 debug
#+end_src
@ -1015,7 +1002,7 @@ data base file.
#+NAME: debug-repl
#+ATTR_LATEX: :width 14cm
#+CAPTION: Pamhyr2 debug Python REPL
#+CAPTION: {{{pamhyr2}}} debug Python REPL
[[./images/python-debug-repl.png]]
* Build the project
@ -1028,7 +1015,7 @@ to build packages manually.
If you need an hand made package, you can script available in
{{{file(packages)}}} directory.
*** GNU/Linux
*** GNU/Linux {{{linux}}}
On GNU/Linux building GNU/Linux packages is easy, you just need python
in version 3.8 must be installed with venv and pyinstaller packages
@ -1050,7 +1037,7 @@ cd packages
./linux.sh
#+end_src
*** Windows
*** Windows {{{windows}}}
To make the Windows packages you have two choice: If you use Windows
you can use the script {{{file(packages/windows.bat)}}}, other else
@ -1065,10 +1052,10 @@ winetricks installed.
** Setup the CI environment
Pamhyr2 need a Linux ci-runner and a Windows ci-runner for building
{{{pamhyr2}}} need a Linux ci-runner and a Windows ci-runner for building
package. The windows ci-runner could run on a Wine environement.
*** Linux
*** Linux {{{linux}}}
The Linux ci-runner need some software and dependencies in addtion of
gitlab-ci.
@ -1078,10 +1065,11 @@ gitlab-ci.
emacs emacs-goodies-el \
texlive-full \
python3.8 python3.8-venv
sudo python3 -m pip install pyinstaller
#+end_src
*** Windows (Wine)
*** Windows (Wine) {{{windows}}}
The ci-runner environment for Wine need at least wine version 8, let
[[https://www.numetopia.fr/comment-installer-wine-sur-ubuntu-ou-linux-mint/][see who to add wine official depot to your linux distribution]].
@ -1090,18 +1078,77 @@ The ci-runner environment for Wine need at least wine version 8, let
sudo apt install wine-stable winetricks
#+end_src
In addition, the environment need windows version of:
- [[https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe][Python 3.8.10]]
- Git
- PowerShell
- Gitlab-ci
**** Setup environment
Export Wine environment variable to set wine as 64 bits architecture
and set the correct path for wine environment.
#+begin_src shell
export WINARCH=win64
export WINEPREFIX=$PWD/my-wine-runner-prefix
#+end_src
Setup Wine environment to Windows 10 and install the minimal fonts
with =winetricks=.
#+begin_src shell
winetricks corefonts win10
#+end_src
**** Install dependencies
First install 7zip with help of =winetricks=.
#+begin_src shell
winetricks 7zip
#+end_src
In addition, install in the environment the Windows version of:
- [[https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe][Python 3.8.10]] (ensure the python path is set and Pip is enable)
- [[https://git-scm.com/downloads][Git]]
- [[https://github.com/PowerShell/PowerShell/releases/download/v7.0.1/PowerShell-7.0.1-win-x64.msi][PowerShell]]
- [[https://freefr.dl.sourceforge.net/project/nsis/NSIS%203/3.08/nsis-3.08-setup.exe][Nsis]]
To run a Windows executable into wine environement, use =wine64= command:
#+begin_src shell
wine64 <the-exe-file>
#+end_src
Now, we can install =pyinstaller= on this windows environment:
#+begin_src shell
wine python -m pip install pyinstaller
#+end_src
Now, we can download [[https://docs.gitlab.com/runner/install/windows.html][Gitlab-ci]] runner for Windows an put it in the
current path.
**** Setup runner
You can configure the runner with command:
#+begin_src shell
wine64 gitlab-runner-windows-amd64.exe register
#+end_src
**** Run the runner
Create a new executable shell script =runner.sh= with following lines:
#+begin_src shell
#! /bin/sh
export WINARCH=win64
export WINEPREFIX=$PWD/my-wine-runner-prefix
wine64 gitlab-runner-windows-amd64.exe run
#+end_src
Now you can run the runner with command =./runner.sh=.
{{{bulb}}} You can run this command into a =screen= terminal, detach
the terminal and disconnect from runner machine to keep runner alive.
* Documentation files
This document and the user documentation are org files. This text file
@ -1284,23 +1331,23 @@ Some org-mode configuration used in documentations files are define in
* How to contribute?
Pamhyr2 is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License[fn:license], either
version 3 of the License, or any later version.
{{{pamhyr2}}} is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License[fn:license],
either version 3 of the License, or any later version.
[fn:license] The GPLv3 web page:
https://www.gnu.org/licenses/gpl-3.0.en.html
** Guidelines
To contribute to Pamhyr2, we expect a minimum of respect between
To contribute to {{{pamhyr2}}}, we expect a minimum of respect between
contributors. We therefore ask you to respect the following rules
regarding communication and contribution content:
+ No gender, racial, religious or social discrimination
+ No insults, personal attacks or potentially offensive remarks
+ Pamhyr2 is free software, and intended to remain so, so take care
with the licensing of libraries and external content you want to add
to the project
+ {{{pamhyr2}}} is free software, and intended to remain so, so take
care with the licensing of libraries and external content you want
to add to the project
+ Humour or hidden easter eggs are welcome if they respect the
previous rules
@ -1311,11 +1358,12 @@ an issue on the project's gitlab page[fn:p2-gitlab], or you can create
a merge request on the same page with the changes you have made to the
code, translation or documentation.
The Pamhyr2 copyright is owned by INRAE[fn:inrae], but we keep a
record of each contributors. If you made a modification to pamhyr2
software, please add your name at the end of {{{file(AUTHORS)}}} file
and respect the Listing [[auth-format]] format. You can update this file
information for following contribution.
The {{{pamhyr2}}} copyright is owned by INRAE[fn:inrae], but we keep a
record of each contributors. If you made a modification to
{{{pamhyr2}}} software, please add your name at the end of
{{{file(AUTHORS)}}} file and respect the Listing [[auth-format]]
format. You can update this file information for following
contribution.
#+NAME: auth-format
#+CAPTION: =AUTHORS= file format
@ -1326,16 +1374,17 @@ information for following contribution.
#+CAPTION: Current =AUTHORS= file
#+INCLUDE: "../../AUTHORS" src text
[fn:p2-gitlab] The Pamhyr2 Gitlab project page:
[fn:p2-gitlab] The {{{pamhyr2}}} Gitlab project page:
https://gitlab.irstea.fr/theophile.terraz/pamhyr
[fn:inrae] The INRAE web site: https://www.inrae.fr/
** Translate
You can improve or add translation for the project. To contribute to
Pamhyr2 translate, you need to use Qt Linguist[fn:qt-linguist]. Open
Qt-linguist and edit the translation ({{{file(.ts)}}}) file, finally,
commit the new version of file and make a merge request.
{{{pamhyr2}}} translate, you need to use Qt
Linguist[fn:qt-linguist]. Open Qt-linguist and edit the translation
({{{file(.ts)}}}) file, finally, commit the new version of file and
make a merge request.
If you want add a new language, edit the script
{{{file(src/lang/create_ts.sh)}}} like Listing [[ts-it]]. Run the script
@ -1344,7 +1393,7 @@ and open the new file with Qt-linguist, setup target language (Figure
file and make a merge request.
#+NAME: ts-it
#+CAPTION: Example of modified {{{file(src/lang/create_ts.sh)}}} to add italian (it) translate for Pamhyr2
#+CAPTION: Example of modified {{{file(src/lang/create_ts.sh)}}} to add italian (it) translate for {{{pamhyr2}}}
#+begin_src shell
...
LANG="fr it"
@ -1362,7 +1411,7 @@ https://doc.qt.io/qt-5/qtlinguist-index.html (last access 2023-09-18)
** Code contribution
If you are developper you can improve and/or add features to
Pamhyr2. Please, follow the architecture described in section
{{{pamhyr2}}}. Please, follow the architecture described in section
[[Architecture]] as closely as possible. Keep the code simple, clear and
efficient as possible. The master branch is reserved for the project
maintainer; you can create a new branch or fork the project before the

View File

@ -163,3 +163,7 @@
\SetWatermarkScale{3}
\SetWatermarkFontSize{1cm}
\SetWatermarkText{Work in progress}
%% Icons
\usepackage{fontawesome5}

View File

@ -17,11 +17,15 @@
# -*- coding: utf-8 -*-
# Constant name
#+MACRO: oldPamhyr PAMHyR
#+MACRO: Pamhyr Pamhyr2
#+MACRO: Mage Mage
#+MACRO: Rubarbe RubarBE
#+MACRO: Inrae INRAE
#+MACRO: pamhyr \textsc{PAMHyR}
#+MACRO: pamhyr2 \textsc{Pamhyr2}
#+MACRO: mage \textsc{Mage}
#+MACRO: mage7 \textsc{Mage7}
#+MACRO: mage8 \textsc{Mage8}
#+MACRO: rubarbe \textsc{RubarBE}
#+MACRO: adists \textsc{AdisTS}
#+MACRO: mascaret \textsc{Mascaret}
#+MACRO: inrae INRAE
#+MACRO: latex \LaTeX
# Information
@ -35,6 +39,16 @@
#+MACRO: cite [cite:$1]
#+MACRO: biblio \bibliography{documentation}
# Icons
#+MACRO: bulb \faIcon{lightbulb}
#+MACRO: OK \faIcon{check}
#+MACRO: linux \faIcon{linux}
#+MACRO: windows \faIcon{windows}
#+MACRO: python \faIcon{python}
#+MACRO: java \faIcon{java}
# Wrapper
#+NAME: attr_wrap
#+HEADER: :var width="\\textwidth"

View File

@ -1 +0,0 @@
$Clapet_001 clapet

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -1,3 +1,4 @@
%% LyX 2.0.2 created this file. For more info, see http://www.lyx.org/.
%% Do not edit unless you really know what you are doing.
\documentclass[12pt,french]{article}
@ -8,6 +9,7 @@
\usepackage{textcomp}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage[frenchb]{babel}
\makeatletter
@ -132,17 +134,17 @@ Pendant que vous travaillez sur votre
\section{Créer la structure de la rivière}
Cliquez sur \texttt{[Réseau] => [Éditer le réseau]} ou sur l'icône \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/network.png} pour créer la structure de votre rivière.
Cliquez sur \texttt{[Réseau] => [Modifier le réseau]} ou sur l'icône \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/network.png} pour créer la structure de votre rivière.
Nous voici dans la fenêtre \textit{Réseau}.
Dans cette fenêtre, nous allons définir un graphe orienté qui représente les biefs de notre réseau fluvial : les arêtes sont les biefs, les n½uds sont soit des conditions limites amont, soit des conditions limites aval, soit des jonctions entre biefs.
Dans cette fenêtre, nous allons définir un graphe orienté qui représente les biefs de notre réseau fluvial : les arêtes sont les biefs, les n\oe{}uds sont soit des conditions limites amont, soit des conditions limites aval, soit des jonctions entre biefs.
Un bief par défaut existe dans la nouvelle étude.
Pour les besoins de ce tutoriel, nous allons le supprimer :
cliquez sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/del.png} pour entrer dans le mode \textit{Suppression} puis cliquez sur les n½uds.
cliquez sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/del.png} pour entrer dans le mode \textit{Suppression} puis cliquez sur les n\oe{}uds.
Nous voilà repartis sur une fenêtre vierge.
Appuyez sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/add.png} pour entrer dans le mode \textit{Ajout}. Créez deux n½uds en cliquant dans la zone grise de la fenêtre, et créez un lien en cliquant à nouveau sur chaque n½ud.
Appuyez sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/add.png} pour entrer dans le mode \textit{Ajout}. Créez deux n\oe{}uds en cliquant dans la zone grise de la fenêtre, et créez un lien en cliquant à nouveau sur chaque n\oe{}ud.
Appuyez à nouveau sur \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/add.png} pour quitter le mode \textit{Ajout}.
Vous avez créé votre premier bief, avec un n½ud amont et un n½ud aval.
Dans la partie inférieure de la fenêtre \textit{Réseau}, vous pouvez renommer les n½uds et les biefs.
Vous avez créé votre premier bief, avec un n\oe{}ud amont et un n\oe{}ud aval.
Dans la partie inférieure de la fenêtre \textit{Réseau}, vous pouvez renommer les n\oe{}uds et les biefs.
Comme le bief que nous avons créé est automatiquement sélectionné, toutes les étapes suivantes s'appliqueront à ce bief.
La fenêtre doit se présenter comme suit :
@ -154,7 +156,7 @@ Fermez la fen
\section{Éditer la géométrie de la rivière}
Cliquez sur \texttt{[Géométrie] => [Éditer la géométrie]} ou sur l'icône \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/geometry.png} pour définir la géométrie du bief sélectionné.
Cliquez sur \texttt{[Géométrie] => [Modifier la géométrie]} ou sur l'icône \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/geometry.png} pour définir la géométrie du bief sélectionné.
Pour Importer une géométrie depuis un fichier, cliquez sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/import.png}.
Sélectionnez le fichier \texttt{Data/Bief\_1.st}.
Vous devriez voir :
@ -237,8 +239,8 @@ Nous pouvons cliquer sur la nouvelle ligne pour s
Ici, nous définirons le débit mesuré lors de la crue de février 2002.
Sélectionnez la cellule \textit{Nom} pour donner un nom à la condition limite par exemple "crue2002".
Sélectionner la cellule \textit{Type} et utiliser la combo box pour mettre une loi \textit{Q(t)} : débit en fonction du temps (hydrogramme).
Sélectionnez la cellule \textit{N½ud} et attribuez cette condition au n½ud amont.
Les noms des n½uds sont rappelés dans le panneau de droite, avec une vue du réseau.
Sélectionnez la cellule \textit{n\oe{}ud} et attribuez cette condition au n\oe{}ud amont.
Les noms des n\oe{}uds sont rappelés dans le panneau de droite, avec une vue du réseau.
Sélectionnez maintenant la ligne entière et cliquez sur le bouton d'édition \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"}.
Vous avez ouvert la fenêtre \textit{Éditer les conditions aux limites}.
Dans un éditeur de texte, ouvrez le fichier \texttt{data/Fevrier\_2002.txt}.
@ -250,7 +252,7 @@ Vous pouvez maintenant voir la courbe de d
\par\end{center}
Fermez cette fenêtre. Revenez sur la fenêtre \textit{Conditions aux limites}.
Ajoutez une nouvelle ligne, donnez lui un nom, donnez lui le type \textit{Z(T)} (limnigramme) et associez là au n½ud aval du réseau.
Ajoutez une nouvelle ligne, donnez lui un nom, donnez lui le type \textit{Z(T)} (limnigramme) et associez là au n\oe{}ud aval du réseau.
Ouvrez la fenêtre d'édition des conditions aux limites (\includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"}).
Ajoutez deux lignes et rentrez les valeurs suivantes :
\begin{center}
@ -271,7 +273,7 @@ Vous pouvez fermer les fen
% Cette condition limite se trouve au niveau d'un seuil.
% A cet endroit, l'écoulement passe d'un régime fluvial à un régime torentiel.
% Nous allons donc calculer une courbe de tarage qui correspond au régime critique de l'écoulement au niveau du seuil.
% Sélectionnez la condition limite et ouvrez la fenêtre \textit{Éditer les conditions aux limites} : (\includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"}).
% Sélectionnez la condition limite et ouvrez la fenêtre \textit{Éditer les conditions aux limites} : (\includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"}).
% Dans la fenêtre \textit{Éditer les conditions aux limites} cliquez sur \texttt{[Générer régime critique]} pour générer cette courbe.
% Cliquez ensuite sur \texttt{[Rendre croissant]} pour suprimer les points de la courbe qui ne sont pas strictement croissants.
% Vous pouvez fermer les fenêtres \textit{Éditer les conditions aux limites} et \textit{Conditions aux limites}.
@ -400,19 +402,19 @@ Si vous r
\section{Paramètres du solveur}
Dans la fenêtre principale, cliquez sur \texttt{[Exécuter] => [Parameters numériques des solveurs]}.
Dans la fenêtre principale, cliquez sur \texttt{[Exécuter] => [Paramètres numériques des solveurs]}.
Dans la fenêtre \textit{Paramètres du solveur}, sélectionnez l'onglet \texttt{[Mage v8]}.
Ces paramètres pilotent le comportement du solveur numérique.
la valeur 999:99:00:00 du temps final indique au solveur de s'arrêter lorsqu'il a atteint un régime permanent.
la valeur {\NoAutoSpacing 999:99:00:00} du temps final indique au solveur de s'arrêter lorsqu'il a atteint un régime permanent.
Vous pouvez changer la fréquence d'écriture des résultats dans la ligne \textit{Pas de temps d'écriture dans le fichier .BIN}.
Une valeur inférieure à 1 seconde indique que la valeur de la ligne \textit{Pas de temps d'écriture dans le fichier .TRA} s sera prise à la place.
Une valeur inférieure à 1 seconde indique que la valeur de la ligne \textit{Pas de temps d'écriture dans le fichier .TRA} s sera prise à la place.
% En effet, durant la montée du pic de crue, le solveur a besoin de réduire le pas de temps suffisament pour permettre la convergence des itérations.
Pour accélérer les calculs et pour aider le solveur à démarer, nous allons l'autoriser à dégrader la précision, à l'aide des facteurs de réduction de la précision.
Pour accélérer les calculs et pour aider le solveur à démarrer, nous allons l'autoriser à dégrader la précision, à l'aide des facteurs de réduction de la précision.
Les précisions internes du solveur sont de 10$^{-9}$.
Cette précision est multipliée par le facteur de réduction de la précision : un facteur de 1000 ramènera donc la précision à 10$^{-5}$.
Pour utiliser ce facteur de réduction de la précision, il faut donner un \textit{nombre d'itérations à précision maximum} inférieur au \textit{nombre maximum d'itérations} : le solveur va d'abord tenter de converger avec un certain nombre d'itératons à la précision maximum avant de basculer sur une précision dégradée pour le reste des itérations.
Dans notre cas, c'est nécessaire pour lancer le solveur à partir de la condition initiale calculée par Pamhyr2.
rentrez 1000 dans les trois lignes \textit{facteurs de réduction de la précision}, rentrez 99 pour le \textit{Nombre d'itérations} et 5 pour le \textit{nombre d'itérations à la précision maximum}.
Rentrez 1000 dans les trois lignes \textit{facteurs de réduction de la précision}, rentrez 99 pour le \textit{Nombre maximum d'itérations} et 5 pour le \textit{nombre d'itérations à la précision maximum}.
Gardez les autres paramètres du solveur par défaut.
Fermer la fenêtre \textit{Paramètres du solveur}.
@ -426,12 +428,13 @@ Cette fen
\section{Visualiser les résultats}
il est aussi possible d'ouvrir la fenêtre \textit{Résultats} si vous avez fermé la fenêtre \textit{Log du solveur}, en cliquant sur \texttt{[Résultats] => [Visualiser les derniers résultats]} à partir de la fenêtre principale.
Il est aussi possible d'ouvrir la fenêtre \textit{Résultats} si vous avez fermé la fenêtre \textit{Log du solveur}, en cliquant sur \texttt{[Résultats] => [Visualiser les derniers résultats]} à partir de la fenêtre principale.
Le panneau supérieur vous permet de sélectionner le bief, le panneau inférieur gauche vous permet de sélectionner une section dans ce bief.
Les trois diagrammes sur la droite montrent le bief et la section en travers de la même manière que dans la fenêtre \textit{Géométrie}.
Vous pouvez utiliser le curseur du bas pour visualiser les résultats à différents pas de temps.
Les croix rouges dans les deux vues du haut correspondent aux points ou l'eau déborde de la géométrie au moins une fois dans la simulation.
En pratique il n'y a pas de perte de volume par débordement en dehors du modèle, car le solveur ajoute un mur virtuel aux extrémités des sections.
Pour visualiser le débit, passez à l'onglet \textit{Hydrogramme}.
Pour créer des tracés 2D personnalisés, cliquez sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/add.png} en haut à gauche de la fenêtre.
Sélectionnez les valeurs que vous voulez sur les axes $X$ et $Y$ et cliquez sur \texttt{[OK]}.
@ -440,4 +443,3 @@ Le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/export.p
\pagebreak{}
\end{document}

Binary file not shown.

Binary file not shown.

View File

@ -360,26 +360,11 @@ class InitialConditions(SQLSubModel):
def get_discharge(self):
return self._data_get("discharge")
def _sort_by_z_and_rk(self, profiles):
profiles.sort(
reverse=False,
key=lambda p: p.rk
)
first_z = profiles[0].z()
last_z = profiles[-1].z()
if first_z > last_z:
profiles.sort(
reverse=True,
key=lambda p: p.rk
)
def generate_growing_constant_depth(self, height: float,
compute_discharge: bool):
profiles = self._reach.reach.profiles.copy()
self._sort_by_z_and_rk(profiles)
profiles.reverse()
previous_elevation = -99999.99
@ -433,12 +418,12 @@ class InitialConditions(SQLSubModel):
previous_elevation = elevation
self._data.append(new)
self._generate_resort_data(profiles)
self._data.reverse()
def generate_discharge(self, discharge: float, compute_height: bool):
profiles = self._reach.reach.profiles.copy()
self._sort_by_z_and_rk(profiles)
profiles.reverse()
previous_elevation = -99999.99
@ -491,7 +476,7 @@ class InitialConditions(SQLSubModel):
previous_elevation = elevation
self._data.append(new)
self._generate_resort_data(profiles)
self._data.reverse()
def generate_height(self,
elevation1: float,
@ -525,13 +510,3 @@ class InitialConditions(SQLSubModel):
new["discharge"] = d
new["elevation"] = elevation
self._data.append(new)
def _generate_resort_data(self, profiles):
is_reverse = False
if profiles[0].rk > profiles[-1].rk:
is_reverse = True
self._data.sort(
reverse=not is_reverse,
key=lambda d: d['rk']
)

View File

@ -99,7 +99,10 @@ class CommandLineSolver(AbstractSolver):
params = study.river.get_params(self.type)
args = params.get_by_key("all_command_line_arguments")
return args.split(" ")
if args is None:
return []
else:
return args.split(" ")
def input_param(self):
"""Return input command line parameter(s)

View File

@ -599,29 +599,50 @@ class Mage(CommandLineSolver):
if qlog is not None:
qlog.put("Export VAR file")
with mage_file_open(os.path.join(repertory, f"{name}.VAR"), "w+") as f:
files.append(f"{name}.VAR")
nb_cv = 0
for hs in hydraulic_structures:
if hs.input_reach is None:
continue
for hs in hydraulic_structures:
if hs.input_reach is None:
continue
if not hs.input_reach.is_enable():
continue
if not hs.input_reach.is_enable():
continue
if not hs.enabled:
continue
for bhs in hs.basic_structures:
for bhs in hs.basic_structures:
if bhs.enabled:
logger.info(bhs._type)
if bhs._type != "CV":
if bhs._type == "CV":
nb_cv += 1
if nb_cv != 0:
with mage_file_open(os.path.join(
repertory, f"{name}.VAR"), "w+") as f:
files.append(f"{name}.VAR")
for hs in hydraulic_structures:
if hs.input_reach is None:
continue
name = bhs.name
if name == "":
name = f"HS_{bhs.id:>3}".replace(" ", "0")
if not hs.input_reach.is_enable():
continue
f.write(
f"${name} clapet"
)
if not hs.enabled:
continue
for bhs in hs.basic_structures:
logger.info(bhs._type)
if bhs._type != "CV":
continue
name = bhs.name
if name == "":
name = f"HS_{bhs.id:>3}".replace(" ", "0")
f.write(
f"${name} clapet"
)
return files
def _export_DEV(self, study, repertory, qlog, name="0"):

View File

@ -52,7 +52,7 @@ class PlotRKZ(PamhyrPlot):
self.line_rk_zmin_zmax_highlight = None
self.label_x = self._trad["unit_rk"]
self.label_y = self._trad["unit_height"]
self.label_y = self._trad["unit_depth"]
self.before_plot_selected = None
self.plot_selected = None

View File

@ -77,29 +77,29 @@ class PlotDRK(PamhyrPlot):
rk = self.data.get_rk()
elevation = self.data.get_elevation()
sorted_rk, sorted_elevation = zip(
*sorted(zip(rk, elevation))
)
self.line_rk_elevation = self.canvas.axes.plot(
rk, elevation,
sorted_rk, sorted_elevation,
color=self.color_plot_river_water,
**self.plot_default_kargs
)
z_min = self.data.reach.reach.get_z_min()
geometry_rk = self.data.reach.reach.get_rk()
filtred_elevation = list(
map(
lambda x: elevation[x[0]],
filter(
lambda x: x[1] in geometry_rk,
enumerate(rk)
)
)
sorted_geometry_rk, sorted_z_min = zip(
*sorted(zip(geometry_rk, z_min), reverse=True)
)
self.collection = self.canvas.axes.fill_between(
geometry_rk, z_min, filtred_elevation,
poly_x = sorted_rk + sorted_geometry_rk
poly_y = sorted_elevation + sorted_z_min
self.collection = self.canvas.axes.fill(
poly_x, poly_y,
color=self.color_plot_river_water_zone,
alpha=0.7, interpolate=True
alpha=0.7,
)
@timer

View File

@ -104,15 +104,15 @@ class InitialConditionTableModel(PamhyrTableModel):
row = index.row()
column = index.column()
if self._headers[column] is "speed":
if self._headers[column] == "velocity":
z = self._lst.get(row)["elevation"]
q = self._lst.get(row)["discharge"]
profile = self._reach.reach.get_profiles_from_rk(
self._lst.get(row)["rk"]
)
if len(profile) >= 1:
speed = profile[0].speed(q, z)
return f"{speed:.4f}"
velocity = profile[0].speed(q, z)
return f"{velocity:.4f}"
return ""
elif self._headers[column] not in ["name", "comment"]:

View File

@ -365,40 +365,43 @@ class InitialConditionsWindow(PamhyrWindow):
self._update()
def generate_growing_constant_depth(self):
dlg = DepthDialog(self.depth_value,
self.depth_option,
trad=self._trad,
parent=self)
if dlg.exec():
self.depth_value = dlg.value
self.depth_option = dlg.option
self._table.generate("growing",
self.depth_value,
self.depth_option)
self._update()
def generate_discharge(self):
dlg = DischargeDialog(self.discharge_value,
self.discharge_option,
if self._reach.reach.number_profiles > 0:
dlg = DepthDialog(self.depth_value,
self.depth_option,
trad=self._trad,
parent=self)
if dlg.exec():
self.discharge_value = dlg.value
self.discharge_option = dlg.option
self._table.generate("discharge",
self.discharge_value,
self.discharge_option)
self._update()
if dlg.exec():
self.depth_value = dlg.value
self.depth_option = dlg.option
self._table.generate("growing",
self.depth_value,
self.depth_option)
self._update()
def generate_discharge(self):
if self._reach.reach.number_profiles > 1:
dlg = DischargeDialog(self.discharge_value,
self.discharge_option,
trad=self._trad,
parent=self)
if dlg.exec():
self.discharge_value = dlg.value
self.discharge_option = dlg.option
self._table.generate("discharge",
self.discharge_value,
self.discharge_option)
self._update()
def generate_height(self):
dlg = HeightDialog(self.height_values,
self.height_option,
trad=self._trad,
parent=self)
if dlg.exec():
self.height_values = dlg.values
self.height_option = dlg.option
self._table.generate("height",
self.height_values,
self.height_option)
self._update()
if self._reach.reach.number_profiles > 0:
dlg = HeightDialog(self.height_values,
self.height_option,
trad=self._trad,
parent=self)
if dlg.exec():
self.height_values = dlg.values
self.height_option = dlg.option
self._table.generate("height",
self.height_values,
self.height_option)
self._update()

View File

@ -39,7 +39,7 @@ class ICTranslate(MainTranslate):
"rk": self._dict["unit_rk"],
"discharge": self._dict["unit_discharge"],
"elevation": self._dict["unit_elevation"],
"height": self._dict["unit_height"],
"speed": self._dict["unit_speed"],
"height": self._dict["unit_depth"],
"velocity": self._dict["unit_velocity"],
# "comment": _translate("InitialCondition", "Comment"),
}

View File

@ -20,8 +20,13 @@ import logging
from tools import timer, trace
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.PlotXY import PlotXY
from View.Tools.PamhyrWidget import PamhyrWidget
from PyQt5.QtWidgets import QVBoxLayout
logger = logging.getLogger()
@ -38,7 +43,9 @@ class WidgetInfo(PamhyrWidget):
parent=parent
)
self.parent = parent
self.set_initial_values()
self.setup_graph()
@property
def study(self):
@ -64,6 +71,25 @@ class WidgetInfo(PamhyrWidget):
self.set_label_text("label_lc", "-")
self.set_label_text("label_hs", "-")
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout_xy = self.find(QVBoxLayout, "verticalLayout")
self._toolbar_xy = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self.plot_layout_xy.addWidget(self._toolbar_xy)
self.plot_layout_xy.addWidget(self.canvas)
self.plot = PlotXY(
canvas=self.canvas,
data=None,
trad=self.parent._trad,
toolbar=self._toolbar_xy,
parent=self
)
def update(self):
if self._study is None:
self.set_initial_values()
@ -75,6 +101,15 @@ class WidgetInfo(PamhyrWidget):
self.set_network_values()
self.set_geometry_values()
self.plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
trad=self.parent._trad,
toolbar=self._toolbar_xy,
parent=self
)
self.plot.update()
def set_network_values(self):
river = self._study.river

119
src/View/PlotXY.py Normal file
View File

@ -0,0 +1,119 @@
# PlotXY.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from tools import timer, trace
from View.Tools.PamhyrPlot import PamhyrPlot
from matplotlib import collections
import numpy as np
from PyQt5.QtCore import (
QCoreApplication, Qt, QItemSelectionModel,
QItemSelection, QItemSelectionRange,
)
from PyQt5.QtWidgets import QApplication, QTableView
_translate = QCoreApplication.translate
class PlotXY(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
table=None, parent=None):
super(PlotXY, self).__init__(
canvas=canvas,
trad=trad,
data=data,
toolbar=toolbar,
table=table,
parent=parent
)
self._data = data
self.label_x = self._trad["x"]
self.label_y = self._trad["y"]
self.parent = parent
self._isometric_axis = True
self._auto_relim_update = True
self._autoscale_update = True
@timer
def draw(self):
self.init_axes()
if self._data is None:
self.idle()
return
if len(self._data) < 1:
self.idle()
return
self.line_lr = []
for data in self._data:
if data.reach.number_profiles != 0:
self.draw_xy(data.reach)
self.draw_lr(data.reach)
self.idle()
return
def draw_xy(self, reach):
line_xy = []
for xy in zip(reach.get_x(), reach.get_y()):
line_xy.append(np.column_stack(xy))
line_xy_collection = collections.LineCollection(
line_xy,
colors=self.color_plot_river_bottom
)
self.canvas.axes.add_collection(line_xy_collection)
def draw_lr(self, reach):
lx = []
ly = []
rx = []
ry = []
for x, y in zip(reach.get_x(),
reach.get_y()):
lx.append(x[0])
ly.append(y[0])
rx.append(x[-1])
ry.append(y[-1])
line = self.canvas.axes.plot(
lx, ly,
color=self.color_plot_river_bottom,
linestyle="dotted",
lw=1.,
)
self.line_lr.append(line)
line = self.canvas.axes.plot(
rx, ry,
color=self.color_plot_river_bottom,
linestyle="dotted",
lw=1.,
)
self.line_lr.append(line)
@timer
def update(self):
self.draw()
self.update_idle()

View File

@ -49,8 +49,8 @@ class Plot(PamhyrPlot):
self._mode = mode
self._table_headers = self._trad.get_dict("table_headers")
self.label_x = self._table_headers["z"]
self.label_y = self._table_headers["area"]
self.label_x = self._table_headers["area"]
self.label_y = self._table_headers["z"]
self._isometric_axis = False
@ -71,8 +71,8 @@ class Plot(PamhyrPlot):
self._init = True
def draw_data(self):
x = list(map(lambda v: v[0], self.data.data))
y = list(map(lambda v: v[1], self.data.data))
x = list(map(lambda v: v[1], self.data.data))
y = list(map(lambda v: v[0], self.data.data))
self._line, = self.canvas.axes.plot(
x, y,
color=self.color_plot,
@ -89,7 +89,7 @@ class Plot(PamhyrPlot):
self.update_idle()
def update_data(self):
x = list(map(lambda v: v[0], self.data.data))
y = list(map(lambda v: v[1], self.data.data))
x = list(map(lambda v: v[1], self.data.data))
y = list(map(lambda v: v[0], self.data.data))
self._line.set_data(x, y)

View File

@ -42,6 +42,7 @@ class CustomPlotValuesSelectionDialog(PamhyrDialog):
self._available_values_y = self._trad.get_dict("values_y")
self.setup_radio_buttons()
self.setup_envelop_box()
self.setup_check_boxs()
self.value = None
@ -61,6 +62,24 @@ class CustomPlotValuesSelectionDialog(PamhyrDialog):
self._radio[0][1].setChecked(True)
layout.addStretch()
def setup_envelop_box(self):
self._envelop = []
layout = self.find(QVBoxLayout, "verticalLayout_x")
self._envelop = QCheckBox(
"envelop",
parent=self
)
layout.addWidget(self._envelop)
self._envelop.setChecked(True)
for r in self._radio:
r[1].clicked.connect(self.envelop_box_status)
def envelop_box_status(self):
if self._radio[0][1].isChecked():
self._envelop.setEnabled(True)
else:
self._envelop.setEnabled(False)
def setup_check_boxs(self):
self._check = []
layout = self.find(QVBoxLayout, "verticalLayout_y")
@ -94,6 +113,6 @@ class CustomPlotValuesSelectionDialog(PamhyrDialog):
)
)
self.value = x, y
self.value = x, y, self._envelop.isChecked()
super().accept()

View File

@ -21,6 +21,7 @@ import logging
from functools import reduce
from datetime import datetime
from numpy import sqrt
from numpy import asarray
from tools import timer
from View.Tools.PamhyrPlot import PamhyrPlot
@ -30,11 +31,16 @@ from View.Results.CustomPlot.Translate import CustomPlotTranslate
logger = logging.getLogger()
unit = {
"elevation": "0-meter",
"bed_elevation": "0-meter",
"bed_elevation_envelop": "0-meter",
"water_elevation": "0-meter",
"water_elevation_envelop": "0-meter",
"discharge": "1-m3s",
"discharge_envelop": "1-m3s",
"velocity": "2-ms",
"max_depth": "3-meter",
"velocity_envelop": "2-ms",
"depth": "3-meter",
"depth_envelop": "3-meter",
"mean_depth": "3-meter",
"froude": "4-dimensionless",
"wet_area": "5-m2",
@ -42,7 +48,7 @@ unit = {
class CustomPlot(PamhyrPlot):
def __init__(self, x, y, reach, profile, timestamp,
def __init__(self, x, y, envelop, reach, profile, timestamp,
data=None, canvas=None, trad=None,
toolbar=None, parent=None):
super(CustomPlot, self).__init__(
@ -55,6 +61,7 @@ class CustomPlot(PamhyrPlot):
self._x = x
self._y = y
self._envelop = envelop
self._reach = reach
self._profile = profile
self._timestamp = timestamp
@ -77,11 +84,97 @@ class CustomPlot(PamhyrPlot):
self._axes = {}
def draw_bottom_with_bedload(self, reach):
self._bedrock = self.sl_compute_bedrock(reach)
rk = reach.geometry.get_rk()
z = self.sl_compute_current_z(reach)
return z
def sl_compute_current_z(self, reach):
z_br = self._bedrock
sl = self.sl_compute_current_rk(reach)
z = list(
map(
lambda z, sl: reduce(
lambda z, h: z + h[0],
sl, z
),
z_br, # Bedrock elevation
sl # Current sediment layers
)
)
return z
def sl_compute_bedrock(self, reach):
z_min = reach.geometry.get_z_min()
sl = self.sl_compute_initial(reach)
z = list(
map(
lambda z, sl: reduce(
lambda z, h: z - h[0],
sl, z
),
z_min, # Original geometry
sl # Original sediment layers
)
)
return z
def sl_compute_initial(self, reach):
"""
Get SL list for profile p at initial time (initial data)
"""
t0 = min(list(self.data.get("timestamps")))
return map(
lambda p: p.get_ts_key(t0, "sl")[0],
reach.profiles
)
def sl_compute_current_rk(self, reach):
"""
Get SL list for profile p at current time
"""
return map(
lambda p: p.get_ts_key(self._timestamp, "sl")[0],
reach.profiles
)
def get_ts_zmin(self, profile):
results = self.data
nt = len(list(results.get("timestamps")))
reach = results.river.reach(self._reach)
berdrock = self.sl_compute_bedrock(reach)
sl = reach.profile(profile).get_key("sl")
ts_z_bedrock = [berdrock[profile]]*nt
ts_z_min = list(
map(
lambda z, sl: reduce(
lambda z, h: z + h,
sl, z
),
ts_z_bedrock, # Bedrock elevations
asarray(sl)[:,0,:,0] # Sediment layers
)
)
return ts_z_min
def _draw_rk(self):
results = self.data
reach = results.river.reach(self._reach)
rk = reach.geometry.get_rk()
z_min = reach.geometry.get_z_min()
if reach.has_sediment():
z_min = self.draw_bottom_with_bedload(reach)
else:
z_min = reach.geometry.get_z_min()
q = list(
map(
lambda p: p.get_ts_key(self._timestamp, "Q"),
@ -105,15 +198,45 @@ class CustomPlot(PamhyrPlot):
self._axes[ax].spines['right'].set_position(('outward', shift))
shift += 60
lines = {}
if "elevation" in self._y:
self.lines = {}
if "bed_elevation" in self._y:
ax = self._axes[unit["elevation"]]
ax = self._axes[unit["bed_elevation"]]
line = ax.plot(
rk, z_min,
color='grey', lw=1.,
)
lines["elevation"] = line
self.lines["bed_elevation"] = line
if self._envelop and reach.has_sediment():
ax = self._axes[unit["bed_elevation_envelop"]]
e = list(
map(
lambda p: max(self.get_ts_zmin(p)),
range(len(reach))
)
)
line1 = ax.plot(
rk, e,
color='grey', lw=1.,
linestyle='dotted',
)
self.lines["bed_elevation_envelop"] = line1
e = list(
map(
lambda p: min(self.get_ts_zmin(p)),
range(len(reach))
)
)
line2 = ax.plot(
rk, e,
color='grey', lw=1.,
linestyle='dotted',
)
# self.lines["bed_elevation_envelop"] = line2
if "water_elevation" in self._y:
@ -122,14 +245,46 @@ class CustomPlot(PamhyrPlot):
rk, z, lw=1.,
color='blue',
)
lines["water_elevation"] = line
self.lines["water_elevation"] = line
if "elevation" in self._y:
ax.fill_between(
if "bed_elevation" in self._y:
self.fill = ax.fill_between(
rk, z_min, z,
color='blue', alpha=0.5, interpolate=True
)
if self._envelop:
ax = self._axes[unit["water_elevation_envelop"]]
d = list(
map(
lambda p: max(p.get_key("Z")),
reach.profiles
)
)
line1 = ax.plot(
rk, d, lw=1.,
color='blue',
linestyle='dotted',
)
self.lines["water_elevation_envelop"] = line1
d = list(
map(
lambda p: min(p.get_key("Z")),
reach.profiles
)
)
line2 = ax.plot(
rk, d, lw=1.,
color='blue',
linestyle='dotted',
)
# self.lines["water_elevation_envelop2"] = line2
if "discharge" in self._y:
ax = self._axes[unit["discharge"]]
@ -137,7 +292,37 @@ class CustomPlot(PamhyrPlot):
rk, q, lw=1.,
color='r',
)
lines["discharge"] = line
self.lines["discharge"] = line
if self._envelop:
ax = self._axes[unit["discharge_envelop"]]
q1 = list(
map(
lambda p: max(p.get_key("Q")),
reach.profiles
)
)
line1 = ax.plot(
rk, q1, lw=1.,
color='r',
linestyle='dotted',
)
self.lines["discharge_envelop"] = line1
q2 = list(
map(
lambda p: min(p.get_key("Q")),
reach.profiles
)
)
line2 = ax.plot(
rk, q2, lw=1.,
color='r',
linestyle='dotted',
)
# self.lines["discharge_envelop2"] = line2
if "velocity" in self._y:
@ -155,11 +340,40 @@ class CustomPlot(PamhyrPlot):
rk, v, lw=1.,
color='g',
)
lines["velocity"] = line
self.lines["velocity"] = line
if "max_depth" in self._y:
if self._envelop:
ax = self._axes[unit["max_depth"]]
velocities = list(map(
lambda p: list(map(
lambda q, z:
p.geometry.speed(q, z),
p.get_key("Q"), p.get_key("Z")
)), reach.profiles
)
)
ax = self._axes[unit["velocity_envelop"]]
vmax = [max(v) for v in velocities]
line1 = ax.plot(
rk, vmax, lw=1.,
color='g',
linestyle='dotted',
)
self.lines["velocity_envelop"] = line1
vmin = [min(v) for v in velocities]
line2 = ax.plot(
rk, vmin, lw=1.,
color='g',
linestyle='dotted',
)
# self.lines["velocity_envelop2"] = line2
if "depth" in self._y:
ax = self._axes[unit["depth"]]
d = list(
map(
lambda p: p.geometry.max_water_depth(
@ -171,7 +385,37 @@ class CustomPlot(PamhyrPlot):
rk, d,
color='brown', lw=1.,
)
lines["max_depth"] = line
self.lines["depth"] = line
if self._envelop:
ax = self._axes[unit["depth_envelop"]]
d = list(map(
lambda p1, p2: p1 - p2, map(
lambda p: max(p.get_key("Z")),
reach.profiles
), z_min)
)
line1 = ax.plot(
rk, d,
color='brown', lw=1.,
linestyle='dotted',
)
self.lines["depth_envelop"] = line1
d = list(map(
lambda p1, p2: p1 - p2, map(
lambda p: min(p.get_key("Z")),
reach.profiles
), z_min)
)
line2 = ax.plot(
rk, d,
color='brown', lw=1.,
linestyle='dotted',
)
# self.lines["depth_envelop2"] = line2
if "mean_depth" in self._y:
@ -188,7 +432,7 @@ class CustomPlot(PamhyrPlot):
rk, d,
color='orange', lw=1.,
)
lines["mean_depth"] = line
self.lines["mean_depth"] = line
if "froude" in self._y:
@ -212,7 +456,7 @@ class CustomPlot(PamhyrPlot):
line = ax.plot(
rk, fr, color='black', linestyle='--', lw=1.,
)
lines["froude"] = line
self.lines["froude"] = line
if "wet_area" in self._y:
@ -229,17 +473,115 @@ class CustomPlot(PamhyrPlot):
rk, d,
color='blue', linestyle='--', lw=1.,
)
lines["wet_area"] = line
self.lines["wet_area"] = line
# Legend
lns = reduce(
lambda acc, line: acc + line,
map(lambda line: lines[line], lines),
map(lambda line: self.lines[line], self.lines),
[]
)
labs = list(map(lambda line: self._trad[line], lines))
labs = list(map(lambda line: self._trad[line], self.lines))
self.canvas.axes.legend(lns, labs, loc="best")
def _redraw_rk(self):
results = self.data
reach = results.river.reach(self._reach)
rk = reach.geometry.get_rk()
z_min = reach.geometry.get_z_min()
if reach.has_sediment():
z_min = self.draw_bottom_with_bedload(reach)
else:
z_min = reach.geometry.get_z_min()
q = list(
map(
lambda p: p.get_ts_key(self._timestamp, "Q"),
reach.profiles
)
)
z = list(
map(
lambda p: p.get_ts_key(self._timestamp, "Z"),
reach.profiles
)
)
if "bed_elevation" in self._y:
self.lines["bed_elevation"][0].set_ydata(z_min)
if "water_elevation" in self._y:
self.lines["water_elevation"][0].set_ydata(z)
if "bed_elevation" in self._y:
ax = self._axes[unit["water_elevation"]]
self.fill.remove()
self.fill = ax.fill_between(
rk, z_min, z,
color='blue', alpha=0.5, interpolate=True
)
if "discharge" in self._y:
self.lines["discharge"][0].set_ydata(q)
if "velocity" in self._y:
v = list(
map(
lambda p: p.geometry.speed(
p.get_ts_key(self._timestamp, "Q"),
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
self.lines["discharge"][0].set_ydata(v)
if "depth" in self._y:
d = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
self.lines["depth"][0].set_ydata(d)
if "mean_depth" in self._y:
d = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
self.lines["mean_depth"][0].set_ydata(d)
if "froude" in self._y:
fr = list(
map(
lambda p:
p.geometry.speed(
p.get_ts_key(self._timestamp, "Q"),
p.get_ts_key(self._timestamp, "Z")) /
sqrt(9.81 * (
p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")) /
p.geometry.wet_width(
p.get_ts_key(self._timestamp, "Z"))
)),
reach.profiles
)
)
self.lines["froude"][0].set_ydata(fr)
if "wet_area" in self._y:
d = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
self.lines["wet_area"][0].set_ydata(d)
def _customize_x_axes_time(self, ts, mode="time"):
# Custom time display
nb = len(ts)
@ -301,24 +643,27 @@ class CustomPlot(PamhyrPlot):
q = profile.get_key("Q")
z = profile.get_key("Z")
z_min = profile.geometry.z_min()
ts_z_min = list(
map(
lambda ts: z_min,
ts
if reach.has_sediment():
ts_z_min = self.get_ts_zmin(self._profile)
else:
ts_z_min = list(
map(
lambda ts: z_min,
ts
)
)
)
lines = {}
if "elevation" in self._y:
self.lines = {}
if "bed_elevation" in self._y:
# Z min is constant in time
ax = self._axes[unit["elevation"]]
ax = self._axes[unit["bed_elevation"]]
line = ax.plot(
ts, ts_z_min,
color='grey', lw=1.
)
lines["elevation"] = line
self.lines["bed_elevation"] = line
if "water_elevation" in self._y:
@ -327,11 +672,11 @@ class CustomPlot(PamhyrPlot):
ts, z, lw=1.,
color='b',
)
lines["water_elevation"] = line
self.lines["water_elevation"] = line
if "elevation" in self._y:
if "bed_elevation" in self._y:
ax.fill_between(
self.fill = ax.fill_between(
ts, ts_z_min, z,
color='blue', alpha=0.5, interpolate=True
)
@ -343,7 +688,7 @@ class CustomPlot(PamhyrPlot):
ts, q, lw=1.,
color='r',
)
lines["discharge"] = line
self.lines["discharge"] = line
if "velocity" in self._y:
@ -359,11 +704,11 @@ class CustomPlot(PamhyrPlot):
ts, v, lw=1.,
color='g',
)
lines["velocity"] = line
self.lines["velocity"] = line
if "max_depth" in self._y:
if "depth" in self._y:
ax = self._axes[unit["max_depth"]]
ax = self._axes[unit["depth"]]
d = list(
map(lambda z: profile.geometry.max_water_depth(z), z)
)
@ -372,7 +717,7 @@ class CustomPlot(PamhyrPlot):
ts, d,
color='brown', lw=1.,
)
lines["max_depth"] = line
self.lines["depth"] = line
if "mean_depth" in self._y:
@ -385,7 +730,7 @@ class CustomPlot(PamhyrPlot):
ts, d,
color='orange', lw=1.,
)
lines["mean_depth"] = line
self.lines["mean_depth"] = line
if "froude" in self._y:
@ -402,7 +747,7 @@ class CustomPlot(PamhyrPlot):
line = ax.plot(
ts, d, color='black', linestyle='--', lw=1.,
)
lines["froude"] = line
self.lines["froude"] = line
if "wet_area" in self._y:
@ -414,21 +759,108 @@ class CustomPlot(PamhyrPlot):
line = ax.plot(
ts, d, color='blue', linestyle='--', lw=1.,
)
lines["wet_area"] = line
self.lines["wet_area"] = line
self._customize_x_axes_time(ts)
# Legend
lns = reduce(
lambda acc, line: acc + line,
map(lambda line: lines[line], lines),
map(lambda line: self.lines[line], self.lines),
[]
)
labs = list(map(lambda line: self._trad[line], lines))
labs = list(map(lambda line: self._trad[line], self.lines))
self.canvas.axes.legend(lns, labs, loc="best")
@timer
def _redraw_time(self):
results = self.data
reach = results.river.reach(self._reach)
profile = reach.profile(self._profile)
ts = list(results.get("timestamps"))
ts.sort()
q = profile.get_key("Q")
z = profile.get_key("Z")
if reach.has_sediment():
ts_z_min = self.get_ts_zmin(self._profile)
else:
z_min = profile.geometry.z_min()
ts_z_min = list(
map(
lambda ts: z_min,
ts
)
)
if "bed_elevation" in self._y:
self.lines["bed_elevation"][0].set_ydata(ts_z_min)
if "water_elevation" in self._y:
self.lines["water_elevation"][0].set_ydata(z)
if "bed_elevation" in self._y:
ax = self._axes[unit["bed_elevation"]]
self.fill.remove()
self.fill = ax.fill_between(
ts, ts_z_min, z,
color='blue', alpha=0.5, interpolate=True
)
if "discharge" in self._y:
self.lines["discharge"][0].set_ydata(q)
if "velocity" in self._y:
v = list(
map(
lambda q, z: profile.geometry.speed(q, z),
q, z
)
)
self.lines["velocity"][0].set_ydata(v)
if "depth" in self._y:
d = list(
map(lambda z: profile.geometry.max_water_depth(z), z)
)
self.lines["depth"][0].set_ydata(d)
if "mean_depth" in self._y:
d = list(
map(lambda z: profile.geometry.mean_water_depth(z), z)
)
self.lines["mean_depth"][0].set_ydata(d)
if "froude" in self._y:
d = list(
map(lambda z, q:
profile.geometry.speed(q, z) /
sqrt(9.81 * (
profile.geometry.wet_area(z) /
profile.geometry.wet_width(z))
), z, q)
)
self.lines["froude"][0].set_ydata(d)
if "wet_area" in self._y:
d = list(
map(lambda z: profile.geometry.wet_area(z), z)
)
self.lines["wet_area"][0].set_ydata(d)
self.canvas.axes.relim(visible_only=True)
self.canvas.axes.autoscale_view()
def draw(self):
self.draw_static()
def draw_update(self):
if self._x == "rk":
self._redraw_rk()
elif self._x == "time":
self._redraw_time()
@timer
def draw_static(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
@ -478,16 +910,16 @@ class CustomPlot(PamhyrPlot):
lw=1.,
)
self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None:
self.toolbar.update()
self.canvas.draw_idle()
@timer
def update(self):
if not self._init:
self.draw()
self.draw_current()
return
self.draw_update()
self.draw_current()
# self.draw_static()
return
def set_reach(self, reach_id):
self._reach = reach_id
@ -519,4 +951,5 @@ class CustomPlot(PamhyrPlot):
elif self._x == "time":
x = self._timestamp
self._current.set_data([x, x], self.canvas.axes.get_ylim())
self.canvas.figure.canvas.draw_idle()
self.canvas.draw_idle()

View File

@ -36,14 +36,21 @@ class CustomPlotTranslate(ResultsTranslate):
self._dict['time'] = self._dict["unit_time_s"]
self._dict['rk'] = self._dict["unit_rk"]
self._dict['water_elevation'] = self._dict["unit_water_elevation"]
self._dict['water_elevation_envelop'] = self._dict[
"unit_water_elevation_envelop"
]
self._dict['discharge'] = self._dict["unit_discharge"]
self._dict['elevation'] = _translate(
"CustomPlot", "Bed elevation (m)"
)
self._dict['velocity'] = self._dict["unit_speed"]
self._dict['discharge_envelop'] = self._dict["unit_discharge_envelop"]
self._dict['bed_elevation'] = self._dict["unit_bed_elevation"]
self._dict['bed_elevation_envelop'] = self._dict[
"unit_bed_elevation_envelop"
]
self._dict['velocity'] = self._dict["unit_velocity"]
self._dict['width'] = self._dict["unit_width"]
self._dict['max_depth'] = self._dict["unit_max_height"]
self._dict['mean_depth'] = self._dict["unit_mean_height"]
self._dict['velocity_envelop'] = self._dict["unit_velocity_envelop"]
self._dict['depth'] = self._dict["unit_depth"]
self._dict['depth_envelop'] = self._dict["unit_depth_envelop"]
self._dict['mean_depth'] = self._dict["unit_mean_depth"]
self._dict['wet_area'] = self._dict["unit_wet_area"]
self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"]
self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"]
@ -55,7 +62,7 @@ class CustomPlotTranslate(ResultsTranslate):
"CustomPlot", "Elevation (m)"
)
self._dict['1-m3s'] = self._dict["unit_discharge"]
self._dict['2-ms'] = self._dict["unit_speed"]
self._dict['3-meter'] = self._dict["unit_height"]
self._dict['2-ms'] = self._dict["unit_velocity"]
self._dict['3-meter'] = self._dict["unit_depth"]
self._dict['4-dimensionless'] = self._dict["unit_froude"]
self._dict['5-m2'] = self._dict["wet_area"]

View File

@ -146,8 +146,9 @@ class PlotXY(PamhyrPlot):
return
reach = self.results.river.reach(self._current_reach_id)
reaches = self.results.river.reachs
self.draw_profiles(reach)
self.draw_profiles(reach, reaches)
self.draw_water_elevation(reach)
self.draw_water_elevation_max(reach)
self.draw_guide_lines(reach)
@ -156,12 +157,12 @@ class PlotXY(PamhyrPlot):
self.idle()
self._init = True
def draw_profiles(self, reach):
def draw_profiles(self, reach, reaches):
if reach.geometry.number_profiles == 0:
self._init = False
return
self.line_xy = []
# TODO uncomment to draw all the reaches
# self.draw_other_profiles(reaches)
for xy in zip(reach.geometry.get_x(),
reach.geometry.get_y()):
self.line_xy.append(np.column_stack(xy))
@ -176,6 +177,19 @@ class PlotXY(PamhyrPlot):
)
self.canvas.axes.add_collection(self.line_xy_collection)
def draw_other_profiles(self, reaches):
for reach in reaches:
for xy in zip(reach.geometry.get_x(),
reach.geometry.get_y()):
self.line_xy.append(np.column_stack(xy))
self.line_xy_collection = collections.LineCollection(
self.line_xy,
colors=self.color_plot_river_bottom,
)
self.canvas.axes.add_collection(self.line_xy_collection)
def draw_guide_lines(self, reach):
x_complete = reach.geometry.get_guidelines_x()
y_complete = reach.geometry.get_guidelines_y()

View File

@ -101,7 +101,7 @@ class TableModel(PamhyrTableModel):
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.wet_width(z)
return f"{v:.4f}"
elif self._headers[column] == "max_depth":
elif self._headers[column] == "depth":
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.max_water_depth(z)
return f"{v:.4f}"

View File

@ -490,11 +490,13 @@ class ResultsWindow(PamhyrWindow):
def _add_custom_plot(self):
dlg = CustomPlotValuesSelectionDialog(parent=self)
if dlg.exec():
x, y = dlg.value
self.create_new_tab_custom_plot(x, y)
x, y, envelop = dlg.value
self.create_new_tab_custom_plot(x, y, envelop)
def create_new_tab_custom_plot(self, x: str, y: list):
def create_new_tab_custom_plot(self, x: str, y: list, envelop: bool):
name = f"{x}: {','.join(y)}"
if envelop and x == "rk":
name += "_envelop"
wname = f"tab_custom_{x}_{y}"
tab_widget = self.find(QTabWidget, f"tabWidget")
@ -518,7 +520,7 @@ class ResultsWindow(PamhyrWindow):
)
plot = CustomPlot(
x, y,
x, y, envelop,
self._get_current_reach(),
self._get_current_profile(),
self._get_current_timestamp(),
@ -589,7 +591,7 @@ class ResultsWindow(PamhyrWindow):
dlg = CustomPlotValuesSelectionDialog(parent=self)
if dlg.exec():
x, y = dlg.value
x, y, envelop = dlg.value
else:
return
@ -599,32 +601,39 @@ class ResultsWindow(PamhyrWindow):
)
self.file_dialog(
select_file="AnyFile",
callback=lambda f: self.export_to(f[0], x, y),
callback=lambda f: self.export_to(f[0], x, y, envelop),
default_suffix=".csv",
file_filter=["CSV (*.csv)"],
)
def export_to(self, filename, x, y):
def export_to(self, filename, x, y, envelop):
timestamps = sorted(self._results.get("timestamps"))
reach = self._results.river.reachs[self._get_current_reach()]
first_line = [f"Study: {self._results.study.name}",
f"Reach: {reach.name}"]
if x == "rk":
timestamp = self._get_current_timestamp()
val_dict = self._export_rk(timestamp, y, filename)
first_line.append(f"Time: {timestamp}s")
val_dict = self._export_rk(timestamp, y, envelop, filename)
elif x == "time":
profile = self._get_current_profile()
val_dict = self._export_time(profile, y, filename)
profile_id = self._get_current_profile()
profile = reach.profile(profile_id)
pname = profile.name if profile.name != "" else profile.rk
first_line.append(f"Profile: {pname}")
val_dict = self._export_time(profile_id, y, filename)
with open(filename, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',',
quotechar='|', quoting=csv.QUOTE_MINIMAL)
dict_x = self._trad.get_dict("values_x")
dict_y = self._trad.get_dict("values_y")
header = [dict_x[x]]
for text in y:
header.append(dict_y[text])
header = []
writer.writerow(first_line)
for text in val_dict.keys():
header.append(text)
writer.writerow(header)
for row in range(len(val_dict[x])):
line = [val_dict[x][row]]
for var in y:
for row in range(len(val_dict[dict_x[x]])):
line = []
for var in val_dict.keys():
line.append(val_dict[var][row])
writer.writerow(line)
@ -666,28 +675,60 @@ class ResultsWindow(PamhyrWindow):
self._additional_plot.pop(tab_widget.tabText(index))
tab_widget.removeTab(index)
def _export_rk(self, timestamp, y, filename):
def _export_rk(self, timestamp, y, envelop, filename):
reach = self._results.river.reachs[self._get_current_reach()]
dict_x = self._trad.get_dict("values_x")
dict_y = self._trad.get_dict("values_y")
if envelop:
dict_y.update(self._trad.get_dict("values_y_envelop"))
my_dict = {}
my_dict["rk"] = reach.geometry.get_rk()
if "elevation" in y:
my_dict["elevation"] = reach.geometry.get_z_min()
my_dict[dict_x["rk"]] = reach.geometry.get_rk()
if "bed_elevation" in y:
my_dict[dict_y["bed_elevation"]] = reach.geometry.get_z_min()
#if envelop and reach.has_sediment():
if "discharge" in y:
my_dict["discharge"] = list(
my_dict[dict_y["discharge"]] = list(
map(
lambda p: p.get_ts_key(timestamp, "Q"),
reach.profiles
)
)
if envelop:
my_dict[dict_y["min_discharge"]] = list(
map(
lambda p: min(p.get_key("Q")),
reach.profiles
)
)
my_dict[dict_y["max_discharge"]] = list(
map(
lambda p: max(p.get_key("Q")),
reach.profiles
)
)
if "water_elevation" in y:
my_dict["water_elevation"] = list(
my_dict[dict_y["water_elevation"]] = list(
map(
lambda p: p.get_ts_key(timestamp, "Z"),
reach.profiles
)
)
if envelop:
my_dict[dict_y["min_water_elevation"]] = list(
map(
lambda p: min(p.get_key("Z")),
reach.profiles
)
)
my_dict[dict_y["max_water_elevation"]] = list(
map(
lambda p: max(p.get_key("Z")),
reach.profiles
)
)
if "velocity" in y:
my_dict["velocity"] = list(
my_dict[dict_y["velocity"]] = list(
map(
lambda p: p.geometry.speed(
p.get_ts_key(timestamp, "Q"),
@ -695,16 +736,44 @@ class ResultsWindow(PamhyrWindow):
reach.profiles
)
)
if "max_depth" in y:
my_dict["max_depth"] = list(
if envelop:
velocities = list(map(
lambda p: list(map(
lambda q, z:
p.geometry.speed(q, z),
p.get_key("Q"), p.get_key("Z")
)), reach.profiles
)
)
my_dict[dict_y["min_velocity"]] = [min(v) for v in velocities]
my_dict[dict_y["max_velocity"]] = [max(v) for v in velocities]
if "depth" in y:
my_dict[dict_y["depth"]] = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(timestamp, "Z")),
reach.profiles
)
)
if envelop:
my_dict[dict_y["min_depth"]] = list(map(
lambda p1, p2: p1 - p2, map(
lambda p: min(p.get_key("Z")),
reach.profiles
), reach.geometry.get_z_min()
)
)
my_dict[dict_y["max_depth"]] = list(map(
lambda p1, p2: p1 - p2, map(
lambda p: max(p.get_key("Z")),
reach.profiles
), reach.geometry.get_z_min()
)
)
if "mean_depth" in y:
my_dict["mean_depth"] = list(
my_dict[dict_y["mean_depth"]] = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(timestamp, "Z")),
@ -712,7 +781,7 @@ class ResultsWindow(PamhyrWindow):
)
)
if "froude" in y:
my_dict["froude"] = list(
my_dict[dict_y["froude"]] = list(
map(
lambda p:
p.geometry.speed(
@ -728,7 +797,7 @@ class ResultsWindow(PamhyrWindow):
)
)
if "wet_area" in y:
my_dict["wet_area"] = list(
my_dict[dict_y["wet_area"]] = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")),
@ -743,33 +812,36 @@ class ResultsWindow(PamhyrWindow):
profile = reach.profile(profile)
ts = list(self._results.get("timestamps"))
ts.sort()
dict_x = self._trad.get_dict("values_x")
dict_y = self._trad.get_dict("values_y")
my_dict = {}
my_dict["time"] = ts
my_dict[dict_x["time"]] = ts
z = profile.get_key("Z")
q = profile.get_key("Q")
if "elevation" in y:
my_dict["elevation"] = [profile.geometry.z_min()] * len(ts)
if "bed_elevation" in y:
my_dict[dict_y["bed_elevation"]] = [
profile.geometry.z_min()] * len(ts)
if "discharge" in y:
my_dict["discharge"] = q
my_dict[dict_y["discharge"]] = q
if "water_elevation" in y:
my_dict["water_elevation"] = z
my_dict[dict_y["water_elevation"]] = z
if "velocity" in y:
my_dict["velocity"] = list(
my_dict[dict_y["velocity"]] = list(
map(
lambda q, z: profile.geometry.speed(q, z),
q, z
)
)
if "max_depth" in y:
my_dict["max_depth"] = list(
if "depth" in y:
my_dict[dict_y["depth"]] = list(
map(lambda z: profile.geometry.max_water_depth(z), z)
)
if "mean_depth" in y:
my_dict["mean_depth"] = list(
my_dict[dict_y["mean_depth"]] = list(
map(lambda z: profile.geometry.mean_water_depth(z), z)
)
if "froude" in y:
my_dict["froude"] = list(
my_dict[dict_y["froude"]] = list(
map(lambda z, q:
profile.geometry.speed(q, z) /
sqrt(9.81 * (
@ -778,7 +850,7 @@ class ResultsWindow(PamhyrWindow):
), z, q)
)
if "wet_area" in y:
my_dict["wet_area"] = list(
my_dict[dict_y["wet_area"]] = list(
map(lambda z: profile.geometry.wet_area(z), z)
)

View File

@ -72,10 +72,10 @@ class ResultsTranslate(MainTranslate):
"name": _translate("Results", "Profile"),
"water_elevation": self._dict["unit_water_elevation"],
"discharge": self._dict["unit_discharge"],
"velocity": self._dict["unit_speed"],
"velocity": self._dict["unit_velocity"],
"width": self._dict["unit_width"],
"max_depth": self._dict["unit_max_height"],
"mean_depth": self._dict["unit_mean_height"],
"depth": self._dict["unit_depth"],
"mean_depth": self._dict["unit_mean_depth"],
"wet_area": self._dict["unit_wet_area"],
"wet_perimeter": self._dict["unit_wet_perimeter"],
"hydraulic_radius": self._dict["unit_hydraulic_radius"],
@ -88,12 +88,12 @@ class ResultsTranslate(MainTranslate):
}
self._sub_dict["values_y"] = {
"elevation": self._dict["unit_elevation"],
"bed_elevation": self._dict["unit_bed_elevation"],
"water_elevation": self._dict["unit_water_elevation"],
"discharge": self._dict["unit_discharge"],
"velocity": self._dict["unit_speed"],
"max_depth": self._dict["unit_max_height"],
"mean_depth": self._dict["unit_mean_height"],
"velocity": self._dict["unit_velocity"],
"depth": self._dict["unit_depth"],
"mean_depth": self._dict["unit_mean_depth"],
"froude": self._dict["unit_froude"],
"wet_area": self._dict["unit_wet_area"],
}
@ -110,6 +110,18 @@ class ResultsTranslate(MainTranslate):
"name": _translate("Results", "Profile"),
"water_elevation": self._dict["unit_water_elevation"],
"discharge": self._dict["unit_discharge"],
"speed": self._dict["unit_speed"],
"discharge": self._dict["unit_discharge"],
}
self._sub_dict["values_y_envelop"] = {
"min_bed_elevation": self._dict["unit_min_bed_elevation"],
"max_bed_elevation": self._dict["unit_max_bed_elevation"],
"min_water_elevation": self._dict["unit_min_water_elevation"],
"max_water_elevation": self._dict["unit_max_water_elevation"],
"min_discharge": self._dict["unit_min_discharge"],
"max_discharge": self._dict["unit_max_discharge"],
"min_velocity": self._dict["unit_min_velocity"],
"max_velocity": self._dict["unit_max_velocity"],
"min_depth": self._dict["unit_min_depth"],
"max_depth": self._dict["unit_max_depth"],
}

View File

@ -44,7 +44,7 @@ class Plot(PamhyrPlot):
)
self.label_x = self._trad["rk"]
self.label_y = self._trad["height"]
self.label_y = self._trad["elevation"]
self.line_rk_zmin = None
self.line_rk_sl = []

View File

@ -44,7 +44,7 @@ class Plot(PamhyrPlot):
)
self.label_x = self._trad["x"]
self.label_y = self._trad["height"]
self.label_y = self._trad["elevation"]
self.line_rk_zmin = None
self.line_rk_sl = []

View File

@ -30,7 +30,7 @@ class SedimentProfileTranslate(SedimentReachTranslate):
self._dict["x"] = _translate(
"SedimentLayers", "X (m)"
)
self._dict["height"] = self._dict["unit_height"]
self._dict["elevation"] = self._dict["unit_elevation"]
self._dict["Profile sediment layers"] = _translate(
"SedimentLayers", "Profile sediment layers"

View File

@ -35,7 +35,7 @@ class SedimentReachTranslate(SedimentTranslate):
)
self._dict["rk"] = self._dict["unit_rk"]
self._dict["height"] = self._dict["unit_height"]
self._dict["elevation"] = self._dict["unit_elevation"]
self._sub_dict["table_headers"] = {
"name": self._dict["name"],

View File

@ -57,18 +57,65 @@ class UnitTranslate(CommonWordTranslate):
self._dict["unit_rk"] = _translate("Unit", "River Kilometer (m)")
self._dict["unit_width"] = _translate("Unit", "Width (m)")
self._dict["unit_height"] = _translate("Unit", "Depth (m)")
self._dict["unit_max_height"] = _translate("Unit", "Max Depth (m)")
self._dict["unit_mean_height"] = _translate("Unit", "Mean Depth (m)")
self._dict["unit_width_envelop"] = _translate(
"Unit", "Width Envelop (m)"
)
self._dict["unit_max_width"] = _translate("Unit", "Max Width (m)")
self._dict["unit_min_width"] = _translate("Unit", "Min Width (m)")
self._dict["unit_depth"] = _translate("Unit", "Depth (m)")
self._dict["unit_max_depth"] = _translate("Unit", "Max Depth (m)")
self._dict["unit_min_depth"] = _translate("Unit", "Min Depth (m)")
self._dict["unit_depth_envelop"] = _translate(
"Unit", "Depth Envelop (m)"
)
self._dict["unit_mean_depth"] = _translate("Unit", "Mean Depth (m)")
self._dict["unit_diameter"] = _translate("Unit", "Diameter (m)")
self._dict["unit_thickness"] = _translate("Unit", "Thickness (m)")
self._dict["unit_elevation"] = _translate("Unit", "Elevation (m)")
self._dict["unit_water_elevation"] = _translate(
"Unit", "Water elevation (m)"
self._dict["unit_bed_elevation"] = _translate(
"Unit", "Bed Elevation (m)"
)
self._dict["unit_bed_elevation_envelop"] = _translate(
"Unit", "Bed Elevation Envelop (m)"
)
self._dict["unit_max_bed_elevation"] = _translate(
"Unit", "Max Bed Elevation (m)"
)
self._dict["unit_min_bed_elevation"] = _translate(
"Unit", "Min Bed Elevation (m)"
)
self._dict["unit_water_elevation"] = _translate(
"Unit", "Water Elevation (m)"
)
self._dict["unit_water_elevation_envelop"] = _translate(
"Unit", "Water Elevation Envelop (m)"
)
self._dict["unit_max_water_elevation"] = _translate(
"Unit", "Max Water Elevation (m)"
)
self._dict["unit_min_water_elevation"] = _translate(
"Unit", "Min Water Elevation (m)"
)
self._dict["unit_velocity"] = _translate("Unit", "Velocity (m/s)")
self._dict["unit_velocity_envelop"] = _translate(
"Unit", "Velocity Envelop (m/s)"
)
self._dict["unit_max_velocity"] = _translate(
"Unit", "Max Velocity (m/s)"
)
self._dict["unit_min_velocity"] = _translate(
"Unit", "Min Velocity (m/s)"
)
self._dict["unit_speed"] = _translate("Unit", "Velocity (m/s)")
self._dict["unit_discharge"] = _translate("Unit", "Discharge (m^3/s)")
self._dict["unit_discharge_envelop"] = _translate(
"Unit", "Discharge Envelop (m^3/s)"
)
self._dict["unit_max_discharge"] = _translate(
"Unit", "Max Discharge (m^3/s)"
)
self._dict["unit_min_discharge"] = _translate(
"Unit", "Min Discharge (m^3/s)"
)
self._dict["unit_area"] = _translate("Unit", "Area (hectare)")
self._dict["unit_time_s"] = _translate("Unit", "Time (sec)")
@ -146,3 +193,5 @@ class MainTranslate(UnitTranslate):
"MainWindow",
"Do you want to save current study before closing it?"
)
self._dict["x"] = _translate("MainWindow", "X (m)")
self._dict["y"] = _translate("MainWindow", "Y (m)")

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="2.0" language="fr_FR" sourcelanguage="en_150">
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR" sourcelanguage="en_150">
<context>
<name>About</name>
<message>
@ -1167,7 +1168,7 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
</message>
<message>
<location filename="../View/ui/about.ui" line="94"/>
<source>Copyright &#xa9; 2022-2024 INRAE</source>
<source>Copyright © 2022-2024 INRAE</source>
<translation type="obsolete">Copyright © 2022-2024 INRAE</translation>
</message>
<message>
@ -1175,11 +1176,6 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
<source>Version: @version @codename</source>
<translation>Version : @version @codename</translation>
</message>
<message encoding="UTF-8">
<location filename="../View/ui/about.ui" line="94"/>
<source>Copyright © 2022-2024 INRAE</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Frictions</name>
@ -1456,17 +1452,17 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
<context>
<name>MainWindow</name>
<message>
<location filename="../View/Translate.py" line="98"/>
<location filename="../View/Translate.py" line="97"/>
<source>Open debug window</source>
<translation>Ouvrir la fenêtre de débogage</translation>
</message>
<message>
<location filename="../View/Translate.py" line="101"/>
<location filename="../View/Translate.py" line="100"/>
<source>Open SQLite debuging tool (&apos;sqlitebrowser&apos;)</source>
<translation>Ouvrir l&apos;outil de débogage SQLite (&apos;sqlitebrowser&apos;)</translation>
</message>
<message>
<location filename="../View/Translate.py" line="104"/>
<location filename="../View/Translate.py" line="103"/>
<source>Enable this window</source>
<translation>Activer cette fenêtre</translation>
</message>
@ -2311,12 +2307,12 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
<translation>Maillage</translation>
</message>
<message>
<location filename="../View/Translate.py" line="91"/>
<location filename="../View/Translate.py" line="90"/>
<source>Summary</source>
<translation>Résumé</translation>
</message>
<message>
<location filename="../View/Translate.py" line="94"/>
<location filename="../View/Translate.py" line="93"/>
<source>Checks</source>
<translation>Vérifications</translation>
</message>
@ -2536,7 +2532,7 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
<translation>Données</translation>
</message>
<message>
<location filename="../View/Translate.py" line="112"/>
<location filename="../View/Translate.py" line="111"/>
<source>Please select a reach</source>
<translation>Veuillez sélectionner un bief</translation>
</message>
@ -2546,35 +2542,45 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
<translation type="obsolete">L&apos;édition de la géométrie nécessite un bief sélectionné dans le réseau fluvial pour pouvoir travailler dessus</translation>
</message>
<message>
<location filename="../View/Translate.py" line="121"/>
<location filename="../View/Translate.py" line="120"/>
<source>Last open study</source>
<translation>Dernière étude ouverte</translation>
</message>
<message>
<location filename="../View/Translate.py" line="124"/>
<location filename="../View/Translate.py" line="123"/>
<source>Do you want to open again the last open study?</source>
<translation>Voulez-vous rouvrir la dernière étude ?</translation>
</message>
<message>
<location filename="../View/Translate.py" line="115"/>
<location filename="../View/Translate.py" line="114"/>
<source>This edition window need a reach selected into the river network to work on it</source>
<translation>Cette fenêtre d&apos;édition a besoin d&apos;un bief sélectionné dans le réseau pour travailler dessus</translation>
</message>
<message>
<location filename="../View/Translate.py" line="129"/>
<location filename="../View/Translate.py" line="128"/>
<source>Close without saving study</source>
<translation>Fermer sans sauvegarder l&apos;étude</translation>
</message>
<message>
<location filename="../View/Translate.py" line="132"/>
<location filename="../View/Translate.py" line="131"/>
<source>Do you want to save current study before closing it?</source>
<translation>Souhaitez-vous sauvegarder l&apos;étude en cours avant de la fermer ?</translation>
</message>
<message>
<location filename="../View/Translate.py" line="109"/>
<location filename="../View/Translate.py" line="108"/>
<source>Warning</source>
<translation>Avertissement</translation>
</message>
<message>
<location filename="../View/Translate.py" line="135"/>
<source>X (m)</source>
<translation>X (m)</translation>
</message>
<message>
<location filename="../View/Translate.py" line="136"/>
<source>Y (m)</source>
<translation>Y (m)</translation>
</message>
</context>
<context>
<name>MainWindow_reach</name>
@ -3255,17 +3261,17 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
<translation>Vitesse (m/s)</translation>
</message>
<message>
<location filename="../View/Translate.py" line="78"/>
<location filename="../View/Translate.py" line="77"/>
<source>Wet Perimeter (m)</source>
<translation>Périmètre mouillé (m)</translation>
</message>
<message>
<location filename="../View/Translate.py" line="81"/>
<location filename="../View/Translate.py" line="80"/>
<source>Hydraulic Radius (m)</source>
<translation>Rayon hydraulique (m)</translation>
</message>
<message>
<location filename="../View/Translate.py" line="84"/>
<location filename="../View/Translate.py" line="83"/>
<source>Froude number</source>
<translation>Nombre de Froude</translation>
</message>
@ -3277,6 +3283,11 @@ Cette fonctionnalité nécessite un bief muni d&apos;une géométrie.</translati
<message>
<location filename="../View/Translate.py" line="77"/>
<source>Wet Area (m^2)</source>
<translation type="obsolete">Aire mouillée (m²)</translation>
</message>
<message>
<location filename="../View/Translate.py" line="76"/>
<source>Wet Area (m²)</source>
<translation>Aire mouillée (m²)</translation>
</message>
</context>