Compare commits

...
112 Commits
Author SHA1 Message Date
Theophile Terraz 07cae542b0 pep8 2026-02-24 09:18:47 +01:00
Theophile Terraz b693f67b99 debug RubarBE 2026-02-24 09:15:31 +01:00
Theophile Terraz 18a3d51d75 add adists with rubar3 + rubar3 in release 2026-02-23 10:46:05 +01:00
Theophile Terraz 118a39bd5c debug Rubar3 (from scenario branch) 2026-02-09 14:24:09 +01:00
Theophile Terraz 2009e8223b debug adists view 2026-01-30 11:32:06 +01:00
Theophile Terraz bdaa38ea5c Merge branch 'master' of gitlab.com:pamhyr/pamhyr2 2026-01-13 08:58:46 +01:00
Lionel Pénard 713732b1dd Fix doc links in README 2026-01-12 11:03:56 +01:00
Theophile Terraz f053539cdd debug 2025-12-19 11:29:02 +01:00
Theophile Terraz fc6916bb3e lot of debug 2025-12-12 14:18:09 +01:00
Theophile Terraz 97c26cb4fb debug HS 2025-12-12 11:33:13 +01:00
Theophile Terraz 10c98bb96e release for Jerome 2025-12-09 14:25:38 +01:00
Theophile Terraz 7c2d9891d0 debug IC 2025-12-05 16:51:39 +01:00
Theophile Terraz 3c3ac1f1df pep8 2025-12-05 10:19:43 +01:00
Theophile Terraz 80af278a0f add label in custom export results 2025-12-05 10:18:38 +01:00
Theophile Terraz f254eaee25 debug adists 2025-12-02 09:31:07 +01:00
Theophile Terraz 5adb0bdee5 debug GL in resluts 2025-11-28 15:31:07 +01:00
Theophile Terraz 64c99d46d4 debug named points in meshed profiles 2025-11-28 14:12:06 +01:00
Theophile Terraz 0f0defb36f Revert "debug ci"
This reverts commit 1e1ee67d4b.
2025-11-27 16:10:00 +01:00
Theophile Terraz 1e1ee67d4b debug ci 2025-11-27 16:03:09 +01:00
Theophile Terraz 5bc514c565 pep8 2025-11-26 11:58:58 +01:00
Theophile Terraz 1da3461dfe debug meshing 2025-11-26 11:16:19 +01:00
Theophile Terraz dd5eecb1e2 pep8 2025-11-24 15:18:28 +01:00
Theophile Terraz d93917b081 debug internal mesher 2025-11-19 15:27:33 +01:00
Theophile Terraz a31e2127e7 debug old .INI file read 2025-10-23 16:32:04 +02:00
parouby a29df8f904 Network: Center on previous size. 2025-10-21 17:28:16 +02:00
parouby 97ebf9324a Merge branch 'master' of gitlab.com:pamhyr/pamhyr2 2025-10-21 15:42:01 +02:00
parouby 0112ad63d9 Network: Resize network zone. 2025-10-21 15:38:12 +02:00
Theophile Terraz bcbb440ad4 debug import friction 2025-10-21 15:11:28 +02:00
Theophile Terraz 7ef3184699 debug + disable rasterio 2025-10-14 11:52:28 +02:00
Theophile Terraz 5f6c823301 debug results 2025-10-14 11:45:45 +02:00
Theophile Terraz bc4557e21f keep selection after meshing 2025-10-08 09:45:35 +02:00
Theophile Terraz f6e08f203c purge + pep8 2025-10-07 15:28:36 +02:00
Theophile Terraz 5dcbc6b4e1 test clean mage rep 2025-10-07 11:56:01 +02:00
Theophile Terraz ebe906fce0 switch from MailleurTT to internal meshing 2025-10-07 11:52:31 +02:00
Theophile Terraz 911a2bbbff mesh on selection 2025-10-01 10:29:14 +02:00
Theophile Terraz 706a900c8e work on rubar3 2025-09-29 15:25:35 +02:00
Theophile Terraz da03f39425 debug 2025-09-29 11:23:46 +02:00
Theophile Terraz 7d42c164fc debug 2025-09-25 17:01:01 +02:00
Theophile Terraz 1b2f98494b pep8 2025-09-23 15:53:49 +02:00
Theophile Terraz e7b63fe270 debug 2025-09-23 14:55:31 +02:00
Theophile Terraz 80ae4c36ad import lateral contributions from file 2025-09-23 14:17:59 +02:00
Theophile Terraz 41cffd13e0 debug 2025-09-19 16:33:06 +02:00
Theophile Terraz 9270dd0ff1 add HHLab test case 2025-09-19 16:04:10 +02:00
Theophile Terraz ba8a50e0b9 debug frictions 2025-09-19 16:03:23 +02:00
Theophile Terraz fce034419d cleaning 2025-09-18 10:11:49 +02:00
Theophile Terraz 31fe765e51 load frictions from file 2025-09-15 15:40:17 +02:00
Theophile Terraz b582e133a1 load boundary conditions from file 2025-09-12 14:22:46 +02:00
Theophile Terraz 7c4c772f43 load initial state from .INI 2025-09-11 17:15:48 +02:00
Theophile Terraz 5fc4e3903a debug Hogneau case2 2025-09-09 09:22:43 +02:00
Theophile Terraz c07a863018 change Mage repo 2025-07-28 16:46:14 +02:00
Theophile Terraz 243941db51 pep8 2025-06-24 14:19:10 +02:00
Theophile Terraz d6c1316178 undo command for reach selection in geometry 2025-06-24 11:22:00 +02:00
Theophile Terraz f35048b89f add reach selection in geometry 2025-06-23 18:07:46 +02:00
Theophile Terraz 24d92b2ac0 debug 2025-06-16 11:49:54 +02:00
Theophile Terraz c702862bc4 pep8 2025-06-10 09:10:58 +02:00
Theophile Terraz 400f6be9e3 add image icon 2025-06-06 17:53:24 +02:00
Theophile Terraz 5fc6951060 more work on import geo image 2025-06-06 17:32:02 +02:00
Theophile Terraz 5ba38d5cd6 test background img import 2025-06-06 10:16:44 +02:00
Theophile Terraz 3e8132646d debug 2025-06-05 11:01:46 +02:00
Theophile Terraz 5aef0bbf3c work on auto bondary and init cond 2025-05-26 14:53:47 +02:00
Theophile Terraz bb3dadc0c7 debug 2025-05-05 11:50:07 +02:00
Theophile Terraz 7bb176e7c7 debug 2025-05-05 11:32:51 +02:00
Theophile Terraz 8c16e4ed58 add guidelines in res view 2025-04-11 13:58:28 +02:00
Theophile Terraz e9315d9be7 improve previous commit 2025-03-20 11:14:43 +01:00
Theophile Terraz 46738cec6b enable multiple profiles in discharge visualization window 2025-03-19 16:55:09 +01:00
Theophile Terraz e576bb15f1 test gitlab-ci last solver version 2025-03-19 11:21:17 +01:00
Theophile Terraz 26d18d26e9 debug 2025-02-21 14:42:06 +01:00
Theophile Terraz 8bac416ab5 debug AdisTS BCs 2025-02-18 14:54:33 +01:00
Theophile Terraz e6aa840633 debug custom plot (time) 2025-02-17 10:02:44 +01:00
Theophile Terraz 4b976d3b5f add copy/paste in pollutant BC + debug 2025-02-17 09:43:02 +01:00
Theophile Terraz e3315c8f81 add 'cancel' button when closing Pamhyr2 from x button 2025-02-14 14:48:40 +01:00
Theophile Terraz 6bcbd006f1 debug 2025-02-14 14:39:25 +01:00
Theophile Terraz c60a0938eb pep8 2025-02-13 15:10:53 +01:00
Theophile Terraz 4d026afb2c debug CI 2025-02-13 14:49:17 +01:00
Theophile Terraz 4e888d9db0 more debug 2025-02-11 12:16:17 +01:00
Theophile Terraz 440996b636 debug 2025-02-11 11:59:58 +01:00
Theophile Terraz ed1a1a71ac debug 2025-02-11 10:20:12 +01:00
Theophile Terraz cbe3ad0084 debug adists windows 2025-02-06 14:46:34 +01:00
Theophile Terraz 46935fde45 forgotten file 2025-02-04 18:06:11 +01:00
Theophile Terraz ca8dc19c83 work on export CSV compare results 2025-02-04 16:24:42 +01:00
Theophile Terraz e4a31b3497 move custom export to sub folder 2025-02-04 14:31:17 +01:00
Theophile Terraz 406dc2bdd4 update tuto2 2025-02-04 14:07:56 +01:00
Theophile Terraz c325f61fce debug 2025-02-04 13:58:50 +01:00
Theophile Terraz d3729cfe77 cosmetic 2025-02-04 11:50:41 +01:00
Theophile Terraz fa8f07b97e work on multiple results plot 2025-02-04 11:12:56 +01:00
Theophile Terraz 52c153fa84 debug water fill 2025-02-03 11:46:55 +01:00
Theophile Terraz 21bd87f74b debug 2025-02-03 11:17:19 +01:00
Theophile Terraz 4d08f3c271 pep8 2025-02-03 10:09:45 +01:00
Theophile Terraz e44bbd5a3c unit translation 2025-02-03 09:45:19 +01:00
Theophile Terraz f827f6bf13 area unit for reservoirs 2025-02-03 09:34:34 +01:00
Theophile Terraz 4be68ba98d work on custom plots and exports aof compare results 2025-01-31 11:59:08 +01:00
Theophile Terraz e47edea63b debug custom plots (compare resuts) 2025-01-31 10:08:05 +01:00
Theophile Terraz 8daf89d1c4 debug read_bin when .bin does not exist 2025-01-31 10:07:32 +01:00
Theophile Terraz 0fa3ca5fad debug compare results 2025-01-30 12:31:02 +01:00
Theophile Terraz e3229c8183 debug plotH 2025-01-29 15:50:56 +01:00
Theophile Terraz c8606cf445 Merge branch 'master' into compare_results 2025-01-29 14:38:36 +01:00
Theophile Terraz 09029dc993 add copy AdisTS results table 2025-01-29 14:29:28 +01:00
Theophile Terraz 0a97070b90 add copy results table 2025-01-29 14:19:42 +01:00
Theophile Terraz 589b8dcd23 compare_results: debug timestamps 2025-01-27 17:34:47 +01:00
Theophile Terraz 4e87135bf4 compare_results: debug timestamps + disable multiple selection for solver table 2025-01-27 11:48:25 +01:00
Theophile Terraz f636236935 debug open results multiple times 2025-01-27 11:04:13 +01:00
Theophile Terraz c3d732b426 merge master 2025-01-27 09:35:08 +01:00
Theophile Terraz 8913a774ea force Mage sulver type in open from file 2025-01-15 14:50:06 +01:00
Theophile Terraz 36c3785cde Merge branch 'master' into compare_results 2025-01-15 14:27:58 +01:00
Theophile Terraz 5ed905df71 Merge branch 'master' into compare_results 2025-01-15 14:25:12 +01:00
Theophile Terraz ea4fa81cc4 debug 2025-01-15 14:13:46 +01:00
Theophile Terraz 8862be991e Merge branch 'master' into compare_results 2025-01-15 14:08:02 +01:00
Theophile Terraz 8aea55909e debug 2025-01-15 14:07:19 +01:00
Theophile Terraz 1289967abb debug 2025-01-15 14:05:11 +01:00
Theophile Terraz 41d1fb9d59 put compare solvers in results window 2025-01-10 16:54:02 +01:00
Theophile Terraz 8d9a196059 merge 2025-01-09 13:47:26 +01:00
Theophile Terraz 558085705f start to work on compare results 2024-11-28 09:20:20 +01:00
126 changed files with 4875 additions and 948 deletions

No files matched your search

+46 -14
View File
@@ -29,7 +29,9 @@ stages:
############# #############
variables: variables:
MAGE_8_VERSION: "v8.3.6" MAGE_8_VERSION: "permalink/latest"
ADISTS_VERSION: "permalink/latest"
RUBAR_VERSION: "permalink/latest"
dl-mage8-doc: dl-mage8-doc:
stage: downloads stage: downloads
@@ -40,7 +42,7 @@ dl-mage8-doc:
script: script:
- mkdir -p doc - mkdir -p doc
- cd doc - cd doc
- curl -L -o mage8.pdf https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/$MAGE_8_VERSION/downloads/documents/Mage-8_Documentation.pdf - curl -L -o mage8.pdf https://forge.inrae.fr/river-hydraulics/mage/-/releases/$MAGE_8_VERSION/downloads/documents/Mage-8_Documentation.pdf
artifacts: artifacts:
paths: paths:
- doc/mage8.pdf - doc/mage8.pdf
@@ -52,17 +54,13 @@ dl-mage8-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 mage8.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/$MAGE_8_VERSION/downloads/packages/mage_linux.tgz - curl -L -o mage8.tgz https://forge.inrae.fr/river-hydraulics/mage/-/releases/$MAGE_8_VERSION/downloads/packages/mage_linux.tgz
- mkdir -p mage8-linux - mkdir -p mage8-linux
- cd mage8-linux - cd mage8-linux
- tar xvf ../mage8.tgz - tar xvf ../mage8.tgz
artifacts: artifacts:
paths: paths:
- mage8-linux/mage - mage8-linux/mage
- mage8-linux/mage_as7
- mage8-linux/mage_extraire
- mage8-linux/mailleurTT
- mage8-linux/libbief.so
dl-mage8-windows: dl-mage8-windows:
stage: downloads stage: downloads
@@ -71,17 +69,13 @@ dl-mage8-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 mage8.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/$MAGE_8_VERSION/downloads/packages/mage_windows.tgz - curl -L -o mage8.tgz https://forge.inrae.fr/river-hydraulics/mage/-/releases/$MAGE_8_VERSION/downloads/packages/mage_windows.tgz
- mkdir -p mage8-windows - mkdir -p mage8-windows
- cd mage8-windows - cd mage8-windows
- tar xvf ../mage8.tgz - tar xvf ../mage8.tgz
artifacts: artifacts:
paths: paths:
- mage8-windows/mage.exe - mage8-windows/mage.exe
- mage8-windows/mage_as7.exe
- mage8-windows/mage_extraire.exe
- mage8-windows/mailleurTT.exe
- mage8-windows/libbief.dll
dl-adists-linux: dl-adists-linux:
stage: downloads stage: downloads
@@ -92,7 +86,7 @@ dl-adists-linux:
script: script:
- mkdir -p adists-linux - mkdir -p adists-linux
- cd adists-linux - cd adists-linux
- curl -L -o adists https://gitlab.irstea.fr/jean-baptiste.faure/adists/-/releases/v1.0.1/downloads/executables/adists - curl -L -o adists https://forge.inrae.fr/river-hydraulics/adists/-/releases/$ADISTS_VERSION/downloads/executables/adists
artifacts: artifacts:
paths: paths:
- adists-linux/adists - adists-linux/adists
@@ -106,11 +100,42 @@ dl-adists-windows:
script: script:
- mkdir -p adists-windows - mkdir -p adists-windows
- cd adists-windows - cd adists-windows
- curl -L -o adists.exe https://gitlab.irstea.fr/jean-baptiste.faure/adists/-/releases/v1.0.1/downloads/executables/adists_win64.exe - curl -L -o adists.exe https://forge.inrae.fr/river-hydraulics/adists/-/releases/$ADISTS_VERSION/downloads/executables/adists_win64.exe
artifacts: artifacts:
paths: paths:
- adists-windows/adists.exe - adists-windows/adists.exe
dl-rubar-linux:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
script:
- mkdir -p rubar-linux
- cd rubar-linux
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_linux.tgz
- tar xf rubar.tgz
artifacts:
paths:
- rubar-linux/rubarbe
dl-rubar-windows:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
script:
- mkdir -p rubar-windows
- cd rubar-windows
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_windows.tgz
- tar xf rubar.tgz
artifacts:
paths:
- rubar-windows/rubarbe.exe
############# #############
# CONFIGURE # # CONFIGURE #
############# #############
@@ -273,6 +298,8 @@ build-linux:
needs: needs:
- job: dl-adists-linux - job: dl-adists-linux
artifacts: true artifacts: true
- job: dl-rubar-linux
artifacts: true
- job: dl-mage8-linux - job: dl-mage8-linux
artifacts: true artifacts: true
- job: dl-mage8-doc - job: dl-mage8-doc
@@ -314,6 +341,9 @@ build-linux:
# Copy adists # Copy adists
- mkdir -p pamhyr/adists - mkdir -p pamhyr/adists
- cp -v ../adists-linux/* pamhyr/adists/ - cp -v ../adists-linux/* pamhyr/adists/
# Copy rubar
- mkdir -p pamhyr/rubar
- cp -v ../rubar-linux/* pamhyr/rubar/
# Copy Pamhyr # Copy Pamhyr
- cp -r dist/pamhyr/* pamhyr/ - cp -r dist/pamhyr/* pamhyr/
# Pamhyr script to force x11 # Pamhyr script to force x11
@@ -379,6 +409,8 @@ build-windows:
artifacts: true artifacts: true
- job: dl-adists-windows - job: dl-adists-windows
artifacts: true artifacts: true
- job: dl-rubar-windows
artifacts: true
- job: dl-mage8-doc - job: dl-mage8-doc
artifacts: true artifacts: true
- job: set-version - job: set-version
+8 -8
View File
@@ -3,7 +3,7 @@
Pamhyr is a free and open source graphical user interface for 1D hydro-sedimentary Pamhyr is a free and open source graphical user interface for 1D hydro-sedimentary
modelling of rivers. modelling of rivers.
![logo](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/raw/master/src/View/ui/ressources/Pamhyr2_logo.png) ![logo](https://gitlab.com/pamhyr/pamhyr2/-/raw/master/src/View/ui/ressources/Pamhyr2_logo.png)
## Features ## Features
@@ -20,9 +20,9 @@ modelling of rivers.
+ Run Mage 8 on a study and visualize results + Run Mage 8 on a study and visualize results
Let see the Let see the
[documentation](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home) [documentation](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home)
([:fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-fr), ([:fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-fr),
[:gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-en)) [:gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-en))
for more details. for more details.
## Install ## Install
@@ -30,11 +30,11 @@ for more details.
### GNU/Linux ### GNU/Linux
See documentation: See documentation:
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20GNULinux) - [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20GNULinux)
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20GNULinux) - [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20GNULinux)
### Windows ### Windows
See documentation: See documentation:
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20Windows) - [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20Windows)
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20Windows) - [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20Windows)
+1 -1
View File
@@ -1375,7 +1375,7 @@ contribution.
#+INCLUDE: "../../AUTHORS" src text #+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 https://gitlab.com/pamhyr/pamhyr2
[fn:inrae] The INRAE web site: https://www.inrae.fr/ [fn:inrae] The INRAE web site: https://www.inrae.fr/
** Translate ** Translate
@@ -424,7 +424,7 @@ Create a new version of {{{pamhyr}}} called {{{pamhyr2}}}
** Contribution ** Contribution
#+LaTeX: \begin{center} #+LaTeX: \begin{center}
\faIcon{gitlab} https://gitlab.irstea.fr/theophile.terraz/pamhyr/ \faIcon{gitlab} https://gitlab.com/pamhyr/pamhyr2/
#+LaTeX: \end{center} #+LaTeX: \end{center}
*** For users *** For users
@@ -462,7 +462,7 @@ Create a new version of {{{pamhyr}}} called {{{pamhyr2}}}
# #+END_EXPORT # #+END_EXPORT
#+LaTeX: \begin{center} #+LaTeX: \begin{center}
\faIcon{gitlab} https://gitlab.irstea.fr/theophile.terraz/pamhyr/ \faIcon{gitlab} https://gitlab.com/pamhyr/pamhyr2/
#+LaTeX: \end{center} #+LaTeX: \end{center}
#+LaTeX: \begin{center} #+LaTeX: \begin{center}
+41 -11
View File
@@ -112,7 +112,7 @@ Auteurs : & Céline Berni & celine.berni@inrae.fr\tabularnewline
% \pagebreak{} % \pagebreak{}
\section{Préambule} \section*{Préambule}
Dans ce document, nous expliciterons les fonctionnalités de PamHyr utiles à la modélisation de scénarii d'aménagement de la rivière Hogneau comme décrit dans le document de TP. Dans ce document, nous expliciterons les fonctionnalités de PamHyr utiles à la modélisation de scénarii d'aménagement de la rivière Hogneau comme décrit dans le document de TP.
Il est utile au préalable d'avoir suivi le tutoriel pour la modélisation de la rivière Hogneau fourni avec le logiciel (Tuto1-fr.pdf). Il est utile au préalable d'avoir suivi le tutoriel pour la modélisation de la rivière Hogneau fourni avec le logiciel (Tuto1-fr.pdf).
@@ -121,16 +121,36 @@ Suivre le tutoriel 1 vous permettra de construire le modèle avec le scénario d
Notez que l'outil PamHyr ne permet pas au stade actuel de comparer plusieurs scenarii. Notez que l'outil PamHyr ne permet pas au stade actuel de comparer plusieurs scenarii.
Vous devrez pour ce faire exporter les vos résultats à partir de la fenêtre \textit{Résultats} qui s'ouvre en cliquant sur \texttt{[Résultats] => [Visualiser les derniers résultats]} à partir de la fenêtre principale. Vous devrez pour ce faire exporter les vos résultats à partir de la fenêtre \textit{Résultats} qui s'ouvre en cliquant sur \texttt{[Résultats] => [Visualiser les derniers résultats]} à partir de la fenêtre principale.
Le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/export.png} vous permet d'exporter les résultats au format CSV pour une utilisation en dehors de Pamhyr2.
Vous pouvez soit vous placer en un point kilométrique (PK) donné (celui marqué en rouge sur la visualisation du profil en long ou sélectionné dans le tableau) et exporter la hauteur d'eau, le niveau d'eau le débit ou autre en fonction du temps (sélectionnez alors \texttt{Temps (s)} pour l'axe X), soit vous placer à un instant donné et exporter la grandeur de votre choix pour tous les points kilométriques de votre maillage (sélectionnez alors \texttt{Point Kilométrique (m)} pour l'axe X).
Si vous choisissez cette seconde option, cocher \texttt{envelop} vous permettra d'exporter en plus la valeur maximale au cours du temps pour la grandeur choisie.
Ainsi dans l'exemple ci-dessous, les données exportées le seront au point kilométrique 34000\,m, à l'instant $t=1$ jour et 12h.
\begin{center} \begin{center}
\includegraphics[width=15cm]{img/export_resultats.png} \includegraphics[width=15cm]{img/export_resultats_commentee.png}
\includegraphics[width=12cm]{img/export_resultats2.png} %\includegraphics[width=12cm]{../../../src/View/ui/ressources/export_resultats2.png}
\par\end{center} \par\end{center}
Le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/export.png} vous permet d'exporter les résultats au format CSV pour une utilisation en dehors de Pamhyr2.
Deux options s'offrent à vous :
\begin{itemize}
\item Vous souhaitez avoir une vue d'ensemble du cours d'eau (et tracer une courbe de remous par exemple). Vous allez exporter une variable en un instant donné tout le long du cours d'eau. Il faut alors vous placer à un instant donné (à l'aide du curseur en bas à gauche) et exporter la grandeur de votre choix pour tous les points kilométriques de votre maillage. Dans la fenêtre d'export, vous devrez sélectionner \texttt{Point Kilométrique (m)} pour l'axe X, et la variable que vous souhaitez exporter pour l'axe Y.
Cocher \texttt{enveloppe} vous permettra d'exporter en plus les valeurs minimale et maximale au cours du temps pour la grandeur choisie.
Par exemple, vous choisissez d'exporter la cote du fond et la cote de l'eau et vous cochez \texttt{enveloppe}. Vous obtiendrez un tableau avec les colonnes suivantes :
\begin{itemize}
\item \texttt{Point Kilométrique (m)} : toutes les valeurs de PK le long de votre bief
\item \texttt{Cote du fond (m)} : la cote du fond correspondante à chaque PK
\item \texttt{Cote de l'eau (m)} : la cote de l'eau correspondante à chaque PK à l'instant sélectionné (voir figure ci-dessous)
\item \texttt{Cote de l'eau min (m)} : la cote de l'eau minimale atteinte correspondante à chaque PK
\item \texttt{Cote de l'eau max (m)} : la cote de l'eau maximale atteinte correspondante à chaque PK
\end{itemize}
ces deux dernières colonnes constituant l'enveloppe de la cote de l'eau.
\item Vous souhaitez regarder l'évolution au cours du temps de ce qu'il se passe en un point précis du cours d'eau (regarder l'hydrogramme de crue à l'aval d'un aménagement par exemple). Vous devez alors vous placer au point kilométrique (PK) qui vous intéresse (le PK sélectionné est marqué en rouge sur la visualisation du profil en long ou sélectionné dans le tableau) et exporter la hauteur d'eau, le niveau d'eau, le débit ou autre en fonction du temps. Dans la fenêtre d'export, vous devrez sélectionner \texttt{Temps (s)} pour l'axe X.
\end{itemize}
Ainsi dans l'exemple ci-dessus, les données exportées le seront au point kilométrique 34000\,m, à l'instant $t=1$ jour et 12h.
\pagebreak{}
\section{Premier scénario d'aménagement : abaissement du seuil} \section{Premier scénario d'aménagement : abaissement du seuil}
Parmi les 3 options d'aménagement évoquées par la collectivité locale, jouer sur le contrôle aval en abaissant le seuil qui marque la jonction avec le canal de Condé-Pommeroeul peut faire baisser le niveau à l’amont. Parmi les 3 options d'aménagement évoquées par la collectivité locale, jouer sur le contrôle aval en abaissant le seuil qui marque la jonction avec le canal de Condé-Pommeroeul peut faire baisser le niveau à l’amont.
@@ -156,6 +176,8 @@ Sur quelle distance le seuil aval influence-t-il la hauteur d'eau ?
Est-ce suffisant pour limiter la hauteur d'eau au niveau du pont de l'A2 ($PK \approx 33000$\,m) ? Est-ce suffisant pour limiter la hauteur d'eau au niveau du pont de l'A2 ($PK \approx 33000$\,m) ?
\end{remark} \end{remark}
\pagebreak{}
\section{Second scénario d'aménagement : élargissement du lit moyen} \section{Second scénario d'aménagement : élargissement du lit moyen}
Ce scénario d'aménagement consiste à essayer d’augmenter le laminage en créant un lit moyen en rive droite entre les PKs 32 015 et 33 920\,m. Pour rappel, cette localisation est imposée pour des raisons de disponibilité des terrains et on ne peut pas envisager une largeur du lit moyen supérieure à 50 mètres. Ce scénario d'aménagement consiste à essayer d’augmenter le laminage en créant un lit moyen en rive droite entre les PKs 32 015 et 33 920\,m. Pour rappel, cette localisation est imposée pour des raisons de disponibilité des terrains et on ne peut pas envisager une largeur du lit moyen supérieure à 50 mètres.
@@ -166,8 +188,9 @@ Pourquoi élargir le lit moyen peut-il avoir un effet sur le niveau d'eau durant
Pour élargir le lit moyen, la géométrie doit être modifiée. Pour élargir le lit moyen, la géométrie doit être modifiée.
Il est judicieux d'enregistrer votre étude sous un autre nom avant de procéder à ces modifications (cliquez sur \texttt{[Fichier] => [Sauvegarder sous]}). Il est judicieux d'enregistrer votre étude sous un autre nom avant de procéder à ces modifications (cliquez sur \texttt{[Fichier] => [Sauvegarder sous]}).
Le plus simple est de repartir de la géométrie avant maillage avant de modifier les transects.
Vous pouvez supprimer les sections interpolées en les sélectionnant dans la fenêtre \textit{Géométrie} et en appuyant sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/del.png}. Vous aurez un certain nombre de sections à modifier. Pour limiter leur nombre, le plus simple est de repartir de la géométrie initiale (avant maillage) avant de modifier les transects et de mailler à nouveau après les modifications.
Vous pouvez supprimer les sections interpolées (notées interpol\textit{i}) en les sélectionnant dans la fenêtre \textit{Géométrie} et en appuyant sur le bouton \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/del.png}.
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. 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électionnez la section en travers que vous souhaitez modifier. Sélectionnez la section en travers que vous souhaitez modifier.
@@ -197,6 +220,8 @@ Quel est l'effet de cet aménagement sur l'hydrogramme de crue ?
Peut-on s'attendre à ce résultat compte tenu du volume qu'il est possible de stocker en lit moyen et du volume total de la crue ? Peut-on s'attendre à ce résultat compte tenu du volume qu'il est possible de stocker en lit moyen et du volume total de la crue ?
\end{remark} \end{remark}
\pagebreak{}
\section{Troisième scénario d'aménagement : casier de stockage} \section{Troisième scénario d'aménagement : casier de stockage}
Ce troisième scénario consiste à faire baisser le débit dans la rivière en créant un déversement vers un casier de stockage en rive gauche juste en aval du pont de l'A2. Ce troisième scénario consiste à faire baisser le débit dans la rivière en créant un déversement vers un casier de stockage en rive gauche juste en aval du pont de l'A2.
@@ -216,7 +241,7 @@ Cliquez sur \texttt{[Réseau] => [Modifier le réseau]} ou sur l'icône \include
Le réseau pour la configuration de référence ne comportait qu'un seul bief entre deux noeuds. Le réseau pour la configuration de référence ne comportait qu'un seul bief entre deux noeuds.
Pour ajouter un casier, il faut ajouter un noeud pour la diffluence, un noeud en amont du casier et un noeud en aval du casier. Pour ajouter un casier, il faut ajouter un noeud pour la diffluence, un noeud en amont du casier et un noeud en aval du casier.
Le casier ne peut en effet pas être en limite aval du modèle ni directement au niveau de la confluence. Le casier ne peut en effet pas être en limite aval du modèle ni directement au niveau de la confluence.
Pour ajouter un noeud, cliquez \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/add.png} pour sélectionner l'outil ``d'addition'' et cliquez ensuite dans la fenêtre grise à l'endroit ou vous souhaitez ajouter un noeud. Pour ajouter un noeud, cliquez sur \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/add.png} pour sélectionner l'outil ``d'addition'' et cliquez ensuite dans la fenêtre grise à l'endroit ou vous souhaitez ajouter un noeud.
De la même façon vous pourrez avec cet outil ajouter un bief entre deux noeuds en cliquant successivement sur l'un puis sur l'autre. De la même façon vous pourrez avec cet outil ajouter un bief entre deux noeuds en cliquant successivement sur l'un puis sur l'autre.
Vous pourrez alors créer un réseau tel que celui ci-dessous où DevLat est le noeud marquant la diffluence. Vous pourrez alors créer un réseau tel que celui ci-dessous où DevLat est le noeud marquant la diffluence.
\begin{center} \begin{center}
@@ -236,6 +261,11 @@ Il vous faudra supprimer les sections dans l'un que vous implémenterez dans l'a
L'outil d'import de géométrie pourra vous aider. L'outil d'import de géométrie pourra vous aider.
Il faudra le cas échéant réimplémenter également les ouvrages. Il faudra le cas échéant réimplémenter également les ouvrages.
Vous devrez ensuite implémenter le casier sur le noeud nommé casier. Cliquez sur \texttt{[Hydraulique] => [Casiers]}. Vous pouvez ajouter un casier en cliquant sur \includegraphics[width=0.5cm]{../../../src/View/ui/ressources/add.png}. Vous devrez ensuite configurer le casier en cliquant sur l'icône \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"}. Pour chaque altitude, vous pouvez définir l'aire de stockage (en ha). Vous pourrez ainsi configurer votre casier de la façon suivante :
\begin{center}
\includegraphics[width=10cm]{img/Casier.png}
\par\end{center}
Lancez le calcul pour chacune des crues proposées avec ce nouvel aménagement. Lancez le calcul pour chacune des crues proposées avec ce nouvel aménagement.
\begin{remark} \begin{remark}
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

@@ -101,7 +101,7 @@ Authors : & Pierre-Antoine Rouby & pierre-antoine.rouby@inrae.fr\tabularnewline
\section{Install Pamhyr2} \section{Install Pamhyr2}
Pamhyr2 can be downloaded from \url{https://gitlab.irstea.fr/theophile.terraz/pamhyr}. Pamhyr2 can be downloaded from \url{https://gitlab.com/pamhyr/pamhyr2}.
\begin{center} \begin{center}
\includegraphics[width=15cm]{img/dl.png} \includegraphics[width=15cm]{img/dl.png}
\par\end{center} \par\end{center}
Binary file not shown.
Binary file not shown.
+2
View File
@@ -12,3 +12,5 @@ shapely>=2.0.1
lxml>=4.9.3 lxml>=4.9.3
platformdirs>=4.2.0 platformdirs>=4.2.0
pyshp>=2.3.1 pyshp>=2.3.1
rasterio==1.3.11
#fortranformat==2.0.3
+4
View File
@@ -46,6 +46,10 @@ copy /y ..\mage8-windows\mage_extraire.exe pamhyr\mage8\
copy /y ..\mage8-windows\mailleurTT.exe pamhyr\mage8\ copy /y ..\mage8-windows\mailleurTT.exe pamhyr\mage8\
copy /y ..\mage8-windows\libbief.dll pamhyr\mage8\ copy /y ..\mage8-windows\libbief.dll pamhyr\mage8\
rem rubar
mkdir pamhyr\rubar
copy /y ..\rubar-windows\rubarbe.exe pamhyr\rubar\
rem adists rem adists
mkdir pamhyr\adists mkdir pamhyr\adists
copy /y ..\adists-windows\adists.exe pamhyr\adists\ copy /y ..\adists-windows\adists.exe pamhyr\adists\
+2
View File
@@ -12,3 +12,5 @@ shapely>=2.0.1
lxml>=4.9.3 lxml>=4.9.3
platformdirs>=4.2.0 platformdirs>=4.2.0
pyshp>=2.3.1 pyshp>=2.3.1
rasterio==1.3.11
#fortranformat==2.0.3
+317
View File
@@ -0,0 +1,317 @@
# Internal.py -- Pamhyr
# Copyright (C) 2023-2025 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 numpy import sign
from copy import deepcopy
from tools import logger_color_red, logger_color_reset, logger_exception
from Meshing.AMeshingTool import AMeshingTool
from Model.Geometry.ProfileXYZ import ProfileXYZ
logger = logging.getLogger()
class InternalMeshing(AMeshingTool):
def __init__(self):
super(InternalMeshing, self).__init__()
###########
# Meshing #
###########
def meshing(self, reach,
step: float = 50,
limites=[-1, -1],
linear: bool = True):
if reach is None or len(reach.profiles) == 0:
return reach
if limites[0] > limites[1]:
lim = limites[1]
limites[1] = limites[0]
limites[0] = lim
elif limites[0] == limites[1]:
return reach
self.st_to_m(reach, limites)
self.interpolate_transversal_step(reach,
limites,
step)
self.purge_aligned_points(reach)
return reach
def st_to_m(self, reach, limites):
gl = reach.compute_guidelines()
profiles = reach.profiles[limites[0]:limites[1]+1]
# we make sure that the lines are in the left-to-right order
guide_list = [
x.name
for x in profiles[0].named_points()
if x.name in gl[0]
]
guide_list = ["un"] + guide_list + ["np"]
max_values = [0] * (len(guide_list) - 1)
max_values_index = [0] * (len(guide_list) - 1)
# we work between each couple of guidelines
for j, p in enumerate(profiles):
for i in range(len(guide_list) - 1):
index1 = p.named_point_index(guide_list[i])
index2 = p.named_point_index(guide_list[i+1])
if index2 - index1 > max_values[i]:
max_values[i] = index2 - index1
max_values_index[i] = j
for i in range(len(max_values)):
for isect in range(max_values_index[i], len(profiles)-1):
self.compl_sect(profiles[isect],
profiles[isect+1],
guide_list[i],
guide_list[i+1])
for isect in reversed(range(0, max_values_index[i])):
self.compl_sect(profiles[isect+1],
profiles[isect],
guide_list[i],
guide_list[i+1])
def interpolate_transversal_step(self,
reach,
limites,
step):
# calcul number of intermediate profiles
np = []
for i in range(limites[0], limites[1]):
np.append(int(abs(reach.profiles[i+1].rk -
reach.profiles[i].rk) / step) - 1)
if np[-1] < 0:
np[-1] = 0
d = [] # ratios
p = [] # new profiles
ptr = int(limites[0])
for i in range(limites[1] - limites[0]):
d = 1.0/float(np[i]+1)
ptr0 = ptr
for j in range(np[i]):
p = ProfileXYZ(reach=reach, status=reach._status)
# RATIO entre les deux sections initiales
dj = float(j+1)*d
ptr += 1 # next profile, original
for k in range(len(reach.profiles[ptr0].points)):
p.add()
p.points[k].name = reach.profiles[ptr0].points[k].name
p.points[k].x = reach.profiles[ptr0].points[k].x + \
dj*(reach.profiles[ptr].points[k].x -
reach.profiles[ptr0].points[k].x)
p.points[k].y = reach.profiles[ptr0].points[k].y + \
dj*(reach.profiles[ptr].points[k].y -
reach.profiles[ptr0].points[k].y)
p.points[k].z = reach.profiles[ptr0].points[k].z + \
dj*(reach.profiles[ptr].points[k].z -
reach.profiles[ptr0].points[k].z)
p.rk = reach.profiles[ptr0].rk + \
dj*(reach.profiles[ptr].rk -
reach.profiles[ptr0].rk)
p.name = f'interpol{p.rk}'
reach.insert_profile(ptr, p)
ptr += 1 # next profile, original
def compl_sect(self, sect1, sect2, tag1, tag2):
# sect1: reference section
# sect2: section to complete
# start1 and 2: indices of the first point of the bed to complete
# end = start + len
# len1 and 2: number of intervals
# len1: target len
start1 = sect1.named_point_index(tag1)
start2 = sect2.named_point_index(tag1)
end1 = sect1.named_point_index(tag2)
end2 = sect2.named_point_index(tag2)
len1 = end1 - start1
len2 = end2 - start2
if len1 < len2:
self.compl_sect(sect2, sect1, start2, start1,
len2, len1, tag1, tag2)
return
elif len1 == len2:
return
ltot1 = 0.0
ltot2 = 0.0
alpha = []
beta = []
# remove names
sect2.points[start2].name = ''
sect2.points[end2].name = ''
for i in range(len1):
ltot1 += sect1.point(start1+i).dist(sect1.point(start1+i+1))
alpha.append(ltot1)
alpha = list(map(lambda x: x/ltot1, alpha)) # target ratios
for i in range(len2):
ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1))
beta.append(ltot2)
beta = list(map(lambda x: x/ltot2, beta)) # current ratios
for i in range(len1 - len2):
beta.append(1.0)
beta.append(0.0) # beta[-1]
if len2 < 1 or ltot2 < 0.0001:
# unique point (copy len1 times)
# we are at an edge of the profile
for i in range(len1-len2):
p = sect2.point(start2).copy()
sect2.insert_point(start2, p)
len2 += 1
elif ltot1 < 0.0001:
sect2.add_npoints(len1-len2)
len2 = len1
else: # regular case
# loop on the points to insert
for k in range(len1-len2):
trouve = False
for j0 in range(len2):
if trouve:
break
undeplus = True
for j1 in range(j0, len2):
if undeplus:
if beta[j1] <= alpha[j1]:
undeplus = False
break
if undeplus:
if j0 == len2:
trouve = True
else:
j1 = j0+1
if beta[j0]-alpha[j0] > alpha[j1]-beta[j0]:
trouve = True
if trouve:
len2 += 1
for i in reversed(range(j0, len2)):
beta[i+1] = beta[i]
if (beta[j0] == beta[j0-1]):
# rien
pass
else:
ratio = (alpha[j0] - beta[j0-1]) \
/ (beta[j0] - beta[j0-1])
if ratio < 0.0:
print(f"ratio négatif {ratio}")
# on double le point a gauche
p = sect2.point(start2+j0-1).copy()
sect2.insert_point(start2+j0-1, p)
sect2.point(start2+j0-1).name = ''
beta[j0] = beta[j0-1]
else:
p = sect2.point(start2+j0).copy()
sect2.insert_point(start2+j0, p)
sect2.point(start2+j0+1).name = ''
sect2.point(start2+j0+1).x = \
(1.0-ratio) * sect2.point(start2+j0).x + \
ratio * sect2.point(start2+j0+2).x
sect2.point(start2+j0+1).y = \
(1.0-ratio) * sect2.point(start2+j0).y + \
ratio * sect2.point(start2+j0+2).y
sect2.point(start2+j0+1).z = \
(1.0-ratio) * sect2.point(start2+j0).z + \
ratio * sect2.point(start2+j0+2).z
beta[j0] = (1.-ratio)*beta[j0-1] \
+ ratio*beta[j0+1]
if len1 > len2:
for i in range(len1 - len2):
p = sect2.point(start2+len2).copy()
sect2.insert_point(start2+len2, p)
sect2.point(start2+len2).name = ''
if tag1 != "un":
sect2.point(start2).name = tag1
if tag2 != "np":
sect2.point(start2+len2).name = tag2
sect2.modified()
def update_rk(self, reach,
step: float = 50,
limites=[-1, -1],
origin=0,
directrices=['un', 'np'],
lplan: bool = False,
lm: int = 3,
linear: bool = False,
origin_value=0.0,
orientation=0):
if reach is None or len(reach.profiles) == 0:
return reach
nprof = reach.number_profiles
if origin >= nprof or origin < 0:
logger.warning(
f"Origin outside reach"
)
return reach
# orientation is the orintation of the bief:
# 1: up -> downstream
# 2: down -> upstream
# else: keep current orientation
if orientation == 1:
sgn = 1.0
elif orientation == 2:
sgn = -1.0
else:
sgn = sign(reach.profiles[-1].rk - reach.profiles[0].rk)
if abs(sgn) < 0.5:
sgn = 1.0
reach.profiles[origin].rk = origin_value
if origin < nprof - 1:
for i in range(origin+1, nprof):
# 2D
d1 = reach.profiles[i-1].named_point(
directrices[0]
).dist_2d(reach.profiles[i].named_point(directrices[0]))
d2 = reach.profiles[i-1].named_point(
directrices[1]
).dist_2d(reach.profiles[i].named_point(directrices[1]))
reach.profiles[i].rk = reach.profiles[i-1].rk \
+ (sgn * (d1 + d2) / 2)
if origin > 0:
for i in reversed(range(0, origin)):
# 2D
d1 = reach.profiles[i+1].named_point(
directrices[0]
).dist_2d(reach.profiles[i].named_point(directrices[0]))
d2 = reach.profiles[i+1].named_point(
directrices[1]
).dist_2d(reach.profiles[i].named_point(directrices[1]))
reach.profiles[i].rk = reach.profiles[i+1].rk \
- (sgn * (d1 + d2) / 2)
def purge_aligned_points(self, reach):
for p in reach.profiles:
p.purge_aligned_points()
+1
View File
@@ -19,6 +19,7 @@
import os import os
import logging import logging
import tempfile import tempfile
from numpy import sign
from ctypes import ( from ctypes import (
cdll, cdll,
+1 -1
View File
@@ -45,7 +45,7 @@ class AddFile(SQLSubModel):
self._path = path self._path = path
self._text = text self._text = text
AddFile._id_cnt = max(id, AddFile._id_cnt+1) AddFile._id_cnt = max(self.id + 1, AddFile._id_cnt + 1)
def __getitem__(self, key): def __getitem__(self, key):
value = None value = None
@@ -53,7 +53,8 @@ class BoundaryCondition(SQLSubModel):
self._header = [] self._header = []
self._types = [float, float] self._types = [float, float]
BoundaryCondition._id_cnt = max(BoundaryCondition._id_cnt + 1, self.id) BoundaryCondition._id_cnt = max(BoundaryCondition._id_cnt + 1,
self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
@@ -54,7 +54,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
self._types = [self.time_convert, float] self._types = [self.time_convert, float]
BoundaryConditionAdisTS._id_cnt = max( BoundaryConditionAdisTS._id_cnt = max(
BoundaryConditionAdisTS._id_cnt + 1, self.id) BoundaryConditionAdisTS._id_cnt + 1, self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
@@ -222,3 +222,82 @@ class BoundaryConditionAdisTS(SQLSubModel):
@property @property
def data(self): def data(self):
return self._data.copy() return self._data.copy()
@property
def _default_0(self):
return self._types[0](0)
@property
def _default_1(self):
return self._types[1](0.0)
def new_from_data(self, header, data):
new_0 = self._default_0
new_1 = self._default_1
if len(header) != 0:
for i in [0, 1]:
for j in range(len(header)):
if self._header[i] == header[j]:
if i == 0:
new_0 = self._types[i](data[j].replace(",", "."))
else:
new_1 = self._types[i](data[j].replace(",", "."))
else:
new_0 = self._types[0](data[0].replace(",", "."))
new_1 = self._types[1](data[1].replace(",", "."))
return (new_0, new_1)
def add(self, index: int):
value = (self._default_0, self._default_1)
self._data.insert(index, value)
self._status.modified()
return value
def insert(self, index: int, value):
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 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()
def index(self, bc):
self._data.index(bc)
def get_i(self, index):
return self.data[index]
def get_range(self, _range):
lst = []
for r in _range:
lst.append(r)
return lst
def _set_i_c_v(self, index, column, value):
v = list(self._data[index])
v[column] = self._types[column](value)
self._data[index] = tuple(v)
self._status.modified()
def set_i_0(self, index: int, value):
self._set_i_c_v(index, 0, value)
def set_i_1(self, index: int, value):
self._set_i_c_v(index, 1, value)
+1 -1
View File
@@ -53,7 +53,7 @@ class D90AdisTS(SQLSubModel):
D90AdisTS._id_cnt = max( D90AdisTS._id_cnt = max(
D90AdisTS._id_cnt + 1, D90AdisTS._id_cnt + 1,
self.id self.id + 1
) )
@classmethod @classmethod
+1 -1
View File
@@ -49,7 +49,7 @@ class D90AdisTSSpec(SQLSubModel):
self._d90 = None self._d90 = None
self._enabled = True self._enabled = True
D90AdisTSSpec._id_cnt = max(D90AdisTSSpec._id_cnt + 1, self.id) D90AdisTSSpec._id_cnt = max(D90AdisTSSpec._id_cnt + 1, self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
+1 -1
View File
@@ -57,7 +57,7 @@ class DIFAdisTS(SQLSubModel):
DIFAdisTS._id_cnt = max( DIFAdisTS._id_cnt = max(
DIFAdisTS._id_cnt + 1, DIFAdisTS._id_cnt + 1,
self.id self.id + 1
) )
@classmethod @classmethod
+1 -1
View File
@@ -51,7 +51,7 @@ class DIFAdisTSSpec(SQLSubModel):
self._c = None self._c = None
self._enabled = True self._enabled = True
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id) DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
+1 -1
View File
@@ -41,7 +41,7 @@ class Friction(SQLSubModel):
else: else:
self.id = id self.id = id
Friction._id_cnt = max(self.id, Friction._id_cnt+1) Friction._id_cnt = max(self.id + 1, Friction._id_cnt + 1)
self._name = name self._name = name
self._edge = None self._edge = None
+22
View File
@@ -228,3 +228,25 @@ class PointXYZ(Point, SQLSubModel):
c = PointXYZ.distance(p3, p1) c = PointXYZ.distance(p3, p1)
s = (a + b + c) / 2 s = (a + b + c) / 2
return (s*(s-a) * (s-b)*(s-c)) ** 0.5 return (s*(s-a) * (s-b)*(s-c)) ** 0.5
def dist_from_seg(self, p1, p2):
a2 = pow(PointXYZ.distance(self, p1), 2)
b2 = pow(PointXYZ.distance(self, p2), 2)
c2 = pow(PointXYZ.distance(p1, p2), 2)
if c2 < 0.00000001:
# si les deux points sont confondus
d = 0.0
else:
d = np.sqrt(abs(a2 - pow((c2 - b2 + a2) / (2*np.sqrt(c2)), 2)))
return d
def copy(self):
p = PointXYZ(self.x,
self.y,
self.z,
self.name,
self._profile,
self._status)
p.sl = self.sl
return p
+21 -2
View File
@@ -44,7 +44,7 @@ class Profile(object):
else: else:
self.id = id self.id = id
Profile._id_cnt = max(self.id, Profile._id_cnt+1) Profile._id_cnt = max(self.id + 1, Profile._id_cnt + 1)
self._num = int(num) self._num = int(num)
self._code1 = int(code1) self._code1 = int(code1)
@@ -88,6 +88,25 @@ class Profile(object):
def point(self, index): def point(self, index):
return self.points[index] return self.points[index]
def named_point(self, name):
if name == "un":
return self.points[0]
elif name == "np":
return self.points[-1]
else:
return next((p for p in self.points if p.name == name), None)
def named_point_index(self, name):
if name == "un":
return 0
elif name == "np":
return len(self.points) - 1
else:
return next(
(p for p in enumerate(self.points) if p[1].name == name),
None
)[0]
@property @property
def reach(self): def reach(self):
return self._reach return self._reach
@@ -206,7 +225,7 @@ class Profile(object):
"""Insert point at index. """Insert point at index.
Args: Args:
index: The index of new profile. index: The index of new point.
point: The point. point: The point.
Returns: Returns:
+42 -2
View File
@@ -167,8 +167,7 @@ class ProfileXYZ(Profile, SQLSubModel):
filter( filter(
lambda s: s.id == sl, lambda s: s.id == sl,
data["sediment_layers_list"].sediment_layers data["sediment_layers_list"].sediment_layers
) ), None)
)
data["profile"] = new data["profile"] = new
new._points = PointXYZ._db_load(execute, data.copy()) new._points = PointXYZ._db_load(execute, data.copy())
@@ -883,3 +882,44 @@ class ProfileXYZ(Profile, SQLSubModel):
def modified(self): def modified(self):
self.tab_up_to_date = False self.tab_up_to_date = False
self.station_up_to_date = False self.station_up_to_date = False
def add_npoints(self, npoints):
# add npoints in a profile
for k in range(npoints):
disti = 0.0
i = 1
for j in range(len(self.points)-1):
distj = self.point(j).dist(self.point(j+1))
if distj > disti:
disti = distj
i = j
self.insert_point(i, self.point(i).copy())
self.point(i+1).name = ''
self.point(i+1).x = 0.5 * self.point(i).x + 0.5 * self.point(i+2).x
self.point(i+1).y = 0.5 * self.point(i).y + 0.5 * self.point(i+2).y
self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z
def copy(self):
p = ProfileXYZ(name=self.name,
rk=self.rk,
reach=self.reach,
nb_point=0,
code1=0, code2=0,
status=self.reach._status)
for i, k in enumerate(self.points):
p.insert_point(i, k.copy())
return p
def purge_aligned_points(self):
align = True
while (align):
align = False
for i in range(1, self.number_points - 1):
d = self.point(i).dist_from_seg(self.point(i-1),
self.point(i+1))
if d < 0.00001 and self.point(i).name == "":
align = True
self.delete_i([i])
break
@@ -52,7 +52,7 @@ class BasicHS(SQLSubModel):
self._enabled = True self._enabled = True
self._data = [] self._data = []
BasicHS._id_cnt = max(BasicHS._id_cnt + 1, self.id) BasicHS._id_cnt = max(BasicHS._id_cnt + 1, self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
+2 -2
View File
@@ -83,8 +83,8 @@ class TriangularWeir(BasicHS):
self._data = [ self._data = [
BHSValue("elevation", float, 1.0, status=status), BHSValue("elevation", float, 1.0, status=status),
BHSValue("loading_elevation", float, 9999.999, status=status), BHSValue("loading_elevation", float, 9999.999, status=status),
BHSValue("discharge_coefficient", float, 0.4, status=status), BHSValue("discharge_coefficient", float, 0.31, status=status),
BHSValue("half-angle_tangent", float, 0.0, status=status), BHSValue("half-angle_tangent", float, 1.0, status=status),
] ]
@@ -59,7 +59,7 @@ class HydraulicStructure(SQLSubModel):
HydraulicStructure._id_cnt = max( HydraulicStructure._id_cnt = max(
HydraulicStructure._id_cnt + 1, HydraulicStructure._id_cnt + 1,
self.id self.id + 1
) )
@classmethod @classmethod
@@ -155,7 +155,6 @@ class HydraulicStructure(SQLSubModel):
return new return new
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
print("save hs unit")
execute(f"DELETE FROM hydraulic_structures WHERE id = {self.id}") execute(f"DELETE FROM hydraulic_structures WHERE id = {self.id}")
input_reach_id = -1 input_reach_id = -1
@@ -382,8 +382,12 @@ class InitialConditions(SQLSubModel):
self._data = [] self._data = []
for profile in profiles: for profile in profiles:
width = profile.wet_width(profile.z_min() + height) width = profile.wet_width(profile.z_min() + height)
area = profile.wet_area(profile.z_min() + height)
radius = profile.wet_radius(profile.z_min() + height)
frictions = self._reach.frictions.frictions frictions = self._reach.frictions.frictions
strickler = None strickler = None
if frictions is not None:
if len(frictions) >= 1:
for f in frictions: for f in frictions:
if f.contains_rk(profile.rk): if f.contains_rk(profile.rk):
strickler = f.get_friction(profile.rk)[0] strickler = f.get_friction(profile.rk)[0]
@@ -391,12 +395,18 @@ class InitialConditions(SQLSubModel):
strickler = 25.0 strickler = 25.0
if not compute_discharge: if not compute_discharge:
if profile.rk in data_discharge:
discharge = data_discharge[profile.rk] discharge = data_discharge[profile.rk]
else:
discharge = 0.0
else: else:
discharge = ( discharge = (
((width * 0.8) # ((width * 0.8)
* strickler # * strickler
* (height ** (5/3)) # * (height ** (5/3))
# * (abs(incline) ** (0.5)))
(area * strickler
* (radius ** (2/3))
* (abs(incline) ** (0.5))) * (abs(incline) ** (0.5)))
) )
@@ -423,6 +433,8 @@ class InitialConditions(SQLSubModel):
def generate_discharge(self, discharge: float, compute_height: bool): def generate_discharge(self, discharge: float, compute_height: bool):
profiles = self._reach.reach.profiles.copy() profiles = self._reach.reach.profiles.copy()
if profiles is None:
return None
profiles.reverse() profiles.reverse()
previous_elevation = -99999.99 previous_elevation = -99999.99
@@ -443,6 +455,8 @@ class InitialConditions(SQLSubModel):
width = profile.width_approximation() width = profile.width_approximation()
frictions = self._reach.frictions.frictions frictions = self._reach.frictions.frictions
strickler = None strickler = None
if frictions is not None:
if len(frictions) >= 1:
for f in frictions: for f in frictions:
if f.contains_rk(profile.rk): if f.contains_rk(profile.rk):
strickler = f.get_friction(profile.rk)[0] strickler = f.get_friction(profile.rk)[0]
@@ -450,7 +464,13 @@ class InitialConditions(SQLSubModel):
strickler = 25.0 strickler = 25.0
if not compute_height: if not compute_height:
if profile.rk in data_height:
height = data_height[profile.rk] height = data_height[profile.rk]
else:
height = 0.0
else:
if abs(incline) <= 0:
height = 0.0
else: else:
height = ( height = (
discharge discharge
@@ -499,7 +519,10 @@ class InitialConditions(SQLSubModel):
for profile in profiles: for profile in profiles:
if not compute_discharge: if not compute_discharge:
if profile.rk in data_discharge:
d = data_discharge[profile.rk] d = data_discharge[profile.rk]
else:
d = 0.0
else: else:
d = discharge d = discharge
elevation = interp(profile.rk, elevation = interp(profile.rk,
@@ -58,7 +58,7 @@ class InitialConditionsAdisTS(SQLSubModel):
InitialConditionsAdisTS._id_cnt = max( InitialConditionsAdisTS._id_cnt = max(
InitialConditionsAdisTS._id_cnt + 1, InitialConditionsAdisTS._id_cnt + 1,
self.id self.id + 1
) )
@classmethod @classmethod
@@ -53,7 +53,7 @@ class ICAdisTSSpec(SQLSubModel):
self._rate = None self._rate = None
self._enabled = True self._enabled = True
ICAdisTSSpec._id_cnt = max(ICAdisTSSpec._id_cnt + 1, self.id) ICAdisTSSpec._id_cnt = max(ICAdisTSSpec._id_cnt + 1, self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
@@ -55,7 +55,7 @@ class LateralContribution(SQLSubModel):
self._types = [float, float] self._types = [float, float]
LateralContribution._id_cnt = max( LateralContribution._id_cnt = max(
LateralContribution._id_cnt + 1, self.id) LateralContribution._id_cnt + 1, self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
+2 -1
View File
@@ -31,11 +31,12 @@ class Edge(object):
self._status = status self._status = status
if id == -1: if id == -1:
type(self)._id_cnt += 1
self.id = type(self)._id_cnt self.id = type(self)._id_cnt
else: else:
self.id = id self.id = id
type(self)._id_cnt = max(self.id + 1, type(self)._id_cnt + 1)
self._name = name self._name = name
self.node1 = node1 self.node1 = node1
+2 -1
View File
@@ -30,11 +30,12 @@ class Node(object):
self._status = status self._status = status
if id == -1: if id == -1:
type(self)._id_cnt += 1
self.id = type(self)._id_cnt self.id = type(self)._id_cnt
else: else:
self.id = id self.id = id
type(self)._id_cnt = max(self.id + 1, type(self)._id_cnt + 1)
self._name = name self._name = name
self.pos = Point(x, y) self.pos = Point(x, y)
+1 -1
View File
@@ -52,7 +52,7 @@ class OutputRKAdists(SQLSubModel):
self._enabled = True self._enabled = True
OutputRKAdists._id_cnt = max( OutputRKAdists._id_cnt = max(
OutputRKAdists._id_cnt + 1, self.id) OutputRKAdists._id_cnt + 1, self.id + 1)
@property @property
def reach(self): def reach(self):
+4 -4
View File
@@ -45,13 +45,16 @@ class Pollutants(SQLSubModel):
else: else:
self.id = id self.id = id
if name is None or name == "":
self.name = f"pol{self.id}"
else:
self._name = str(name) self._name = str(name)
self._enabled = True self._enabled = True
self._data = [] self._data = []
Pollutants._id_cnt = max( Pollutants._id_cnt = max(
Pollutants._id_cnt + 1, self.id) Pollutants._id_cnt + 1, self.id + 1)
@property @property
def name(self): def name(self):
@@ -179,7 +182,6 @@ class Pollutants(SQLSubModel):
def new_from_data(self, data): def new_from_data(self, data):
print("from_data before : ", data)
try: try:
new = [int(data[0])] new = [int(data[0])]
new += [float(d) for d in data[1:]] new += [float(d) for d in data[1:]]
@@ -187,8 +189,6 @@ class Pollutants(SQLSubModel):
logger.error(e) logger.error(e)
new = None new = None
print("from_data after : ", new)
return new return new
def __len__(self): def __len__(self):
+1 -1
View File
@@ -45,7 +45,7 @@ class REPLine(SQLSubModel):
self._line = line self._line = line
self._solvers = solvers self._solvers = solvers
REPLine._id_cnt = max(id, REPLine._id_cnt+1) REPLine._id_cnt = max(self.id + 1, REPLine._id_cnt + 1)
def __getitem__(self, key): def __getitem__(self, key):
value = None value = None
+2 -2
View File
@@ -42,7 +42,7 @@ class Reservoir(SQLSubModel):
self._node = None self._node = None
self._data = [] self._data = []
Reservoir._id_cnt = max(Reservoir._id_cnt + 1, self.id) Reservoir._id_cnt = max(Reservoir._id_cnt + 1, self.id + 1)
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
@@ -97,7 +97,7 @@ class Reservoir(SQLSubModel):
new_reservoir._node = next( new_reservoir._node = next(
filter( filter(
lambda n: n.id == node_id, data["nodes"] lambda n: n.id == node_id, data["nodes"]
) ), None
) )
new_data = [] new_data = []
+4
View File
@@ -56,6 +56,10 @@ class Results(object):
def set(self, key, value): def set(self, key, value):
self._meta_data[key] = value self._meta_data[key] = value
@property
def is_valid(self):
return ("timestamps" in self._meta_data)
def get(self, key): def get(self, key):
return self._meta_data[key] return self._meta_data[key]
+13
View File
@@ -57,7 +57,10 @@ class Profile(object):
) )
def get_ts_key(self, timestamp, key): def get_ts_key(self, timestamp, key):
if timestamp in self._data:
if key in self._data[timestamp]:
return self._data[timestamp][key] return self._data[timestamp][key]
return None
def has_sediment(self): def has_sediment(self):
return any(map(lambda ts: "sl" in self._data[ts], self._data)) return any(map(lambda ts: "sl" in self._data[ts], self._data))
@@ -74,6 +77,12 @@ class Reach(object):
) )
) )
self._profile_mask = list(
map(
lambda p: p.name[0:8] != 'interpol', self._profiles
)
)
def __len__(self): def __len__(self):
return len(self._profiles) return len(self._profiles)
@@ -89,6 +98,10 @@ class Reach(object):
def profiles(self): def profiles(self):
return self._profiles.copy() return self._profiles.copy()
@property
def profile_mask(self):
return self._profile_mask
def profile(self, id): def profile(self, id):
return self._profiles[id] return self._profiles[id]
+2 -1
View File
@@ -51,7 +51,7 @@ class Layer(SQLSubModel):
else: else:
self.id = id self.id = id
Layer._id_cnt = max(id, Layer._id_cnt+1) Layer._id_cnt = max(self.id + 1, Layer._id_cnt + 1)
@property @property
def name(self): def name(self):
@@ -277,6 +277,7 @@ class SedimentLayer(SQLSubModel):
"FROM sedimentary_layer " "FROM sedimentary_layer "
) )
if table is not None:
for row in table: for row in table:
sl = cls( sl = cls(
id=row[0], id=row[0],
+1
View File
@@ -276,6 +276,7 @@ class Study(SQLModel):
version = new.execute( version = new.execute(
"SELECT value FROM info WHERE key='study_release'" "SELECT value FROM info WHERE key='study_release'"
) )
if version is not None:
new.status.version = int(version[0]) new.status.version = int(version[0])
# TODO: Load metadata # TODO: Load metadata
+206 -13
View File
@@ -108,6 +108,187 @@ class AdisTS(CommandLineSolver):
name = self._study.name name = self._study.name
return f"{name}.TRA" return f"{name}.TRA"
def _export_ST(self, study, repertory, qlog, name="0"):
files = []
if qlog is not None:
qlog.put("Export ST file")
os.makedirs(os.path.join(repertory, "net"), exist_ok=True)
# Write header
edges = study.river.enable_edges()
for edge in edges:
name = f"Reach_{edge.id + 1:>3}".replace(" ", "0")
with adists_file_open(
os.path.join(repertory, "net", f"{name}.ST"),
"w+"
) as f:
files.append(str(os.path.join("net", f"{name}.ST")))
cnt_num = 1
for profile in edge.reach.profiles:
self._export_ST_profile_header(
f, files, profile, cnt_num
)
cnt_num += 1
# Points
for point in profile.points:
self._export_ST_point_line(
f, files, point
)
# Profile last line
f.write(f" 999.9990 999.9990 999.9990\n")
def _export_ST_profile_header(self, wfile, files,
profile, cnt):
num = f"{cnt:>6}"
c1 = f"{profile.code1:>6}"
c2 = f"{profile.code2:>6}"
t = f"{len(profile.points):>6}"
rk = f"{profile.rk:>12f}"[0:12]
pname = profile.name
if profile.name == "":
# Generate name from profile id prefixed with
# 'p' (and replace space char with '0' char)
pname = f"p{profile.id:>3}".replace(" ", "0")
name = f"{pname:<19}"
# Generate sediment additional data if available
sediment = ""
if profile.sl is not None:
if not any(filter(lambda f: ".GRA" in f, files)):
files.append(self._gra_file)
# Number of layers
nl = len(profile.sl)
sediment = f" {nl:>3}"
# Layers data
for layer in profile.sl.layers:
sediment += (
f" {layer.height:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Profile header line
wfile.write(f"{num}{c1}{c2}{t} {rk} {pname} {sediment}\n")
def _export_ST_point_line(self, wfile, files, point):
x = f"{point.x:<12.4f}"[0:12]
y = f"{point.y:<12.4f}"[0:12]
z = f"{point.z:<12.4f}"[0:12]
n = f"{point.name:<3}"
# Generate sediment additional data if available
sediment = ""
prev = point.z
if point.sl is not None:
# Number of layers
nl = len(point.sl)
sediment = f"{nl:>3}"
# Layers data
for layer in point.sl.layers:
prev = round(prev - layer.height, 5)
sediment += (
f" {prev:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Point line
wfile.write(f"{x} {y} {z} {n} {sediment}\n")
def _export_NET(self, study, repertory, qlog=None, name="0"):
if qlog is not None:
qlog.put("Export NET file")
with adists_file_open(
os.path.join(repertory, f"{name}.NET"), "w+") as f:
edges = study.river.enable_edges()
for e in edges:
name = f"Reach_{e.id + 1:>3}".replace(" ", "0")
id = name
n1 = f"{e.node1.id:3}".replace(" ", "x")
n2 = f"{e.node2.id:3}".replace(" ", "x")
file = os.path.join("net", name + ".ST")
f.write(f"{id} {n1} {n2} {file}\n")
def _export_fake_INI(self, study, repertory, qlog=None, name="0"):
if qlog is not None:
qlog.put("Export fake INI file")
with adists_file_open(
os.path.join(
repertory, "Mage_fin.ini"
), "w+"
) as f:
edges = study.river.enable_edges()
for i, edge in enumerate(edges):
lst = list(filter(
lambda f: f.is_full_defined(),
edge.frictions.frictions
))
rk_min = 9999999.9
rk_max = -9999999.9
coeff_min = -1.0
coeff_max = -1.0
for s in lst: # TODO optimise ?
if s.begin_rk > rk_max:
rk_max = s.begin_rk
coeff_max = s.begin_strickler
if s.begin_rk < rk_min:
rk_min = s.begin_rk
coeff_min = s.begin_strickler
if s.end_rk > rk_max:
rk_max = s.end_rk
coeff_max = s.end_strickler
if s.end_rk < rk_min:
rk_min = s.end_rk
coeff_min = s.end_strickler
print("min max", rk_min, rk_max)
def get_stricklers_from_rk(rk, lst):
print("rk", rk)
coeff = None
if rk > rk_max:
coeff = coeff_max
elif rk < rk_min:
coeff = coeff_min
else:
for s in lst:
if (rk >= s.begin_rk and rk <= s.end_rk or
rk <= s.begin_rk and rk >= s.end_rk):
coeff = s.begin_strickler # TODO: inerpolate
break
# TODO interpolation if rk is not in frictons
if coeff is None:
logger.error(
"Study frictions are not fully defined"
)
return None
return coeff.minor, coeff.medium
for j, profile in enumerate(edge.reach.profiles):
coef_min, coef_moy = get_stricklers_from_rk(profile.rk,
lst)
f.write(
f" {i+1:3}{j+1:4}{' '*116}{coef_min:9}{coef_moy:9}\n")
def _export_REP_additional_lines(self, study, rep_file): def _export_REP_additional_lines(self, study, rep_file):
lines = filter( lines = filter(
lambda line: line.is_enabled(), lambda line: line.is_enabled(),
@@ -128,9 +309,9 @@ class AdisTS(CommandLineSolver):
repertory, f"{name}.REP" repertory, f"{name}.REP"
), "w+" ), "w+"
) as f: ) as f:
path = os.path.join("..", mage_rep, name)
f.write(f"NET ../{mage_rep}/{name}.NET\n") f.write(f"NET {name}.NET\n")
f.write(f"REP ../{mage_rep}/{name}.REP\n") f.write(f"REP {path}.REP\n")
for file in files: for file in files:
EXT = file.split('.')[1] EXT = file.split('.')[1]
@@ -138,13 +319,25 @@ class AdisTS(CommandLineSolver):
self._export_REP_additional_lines(study, f) self._export_REP_additional_lines(study, f)
path_mage_net = os.path.join(os.path.abspath( self._export_ST(study, repertory, qlog, name=name)
os.path.join(repertory, os.pardir) self._export_NET(study, repertory, qlog, name=name)
), f"{mage_rep}/net")
path_adists_net = os.path.join(repertory, "net")
if os.path.exists(path_mage_net): # fake mage_fin.ini:
shutil.copytree(path_mage_net, path_adists_net, dirs_exist_ok=True)
path_mage = os.path.join(os.path.abspath(
os.path.join(repertory, os.pardir)), mage_rep)
self._export_fake_INI(study, path_mage,
qlog, name=name)
# path_mage_net = os.path.join(os.path.abspath(
# os.path.join(repertory, os.pardir)
# ), os.path.join(mage_rep, "net"))
# path_adists_net = os.path.join(repertory, "net")
# if os.path.exists(path_mage_net):
# shutil.copytree(path_mage_net,
# path_adists_net,
# dirs_exist_ok=True)
@timer @timer
def export(self, study, repertory, qlog=None): def export(self, study, repertory, qlog=None):
@@ -197,7 +390,7 @@ class AdisTS(CommandLineSolver):
_nodes_views = set() _nodes_views = set()
def get_reach_name(self, reach): def get_reach_name(self, reach):
return f"Reach_{reach.id:>3}".replace(" ", "0") return f"Reach_{reach.id + 1:>3}".replace(" ", "0")
def get_node_name(self, node): def get_node_name(self, node):
"""Generate a 3 char name for node """Generate a 3 char name for node
@@ -338,9 +531,9 @@ class AdisTSwc(AdisTS):
) as f: ) as f:
for LC in POL_LC: for LC in POL_LC:
reach = next(filter( reach = next(filter(
lambda edge: edge.id == LC.edge, study.river.edges() lambda edge: edge.id == LC.edge, study.river.enable_edges()
)) # .name )) # .name
reach_name = self.get_reach_name(self, reach) reach_name = self.get_reach_name(reach)
f.write(f"${reach_name} {LC.begin_rk} {LC.end_rk}\n") f.write(f"${reach_name} {LC.begin_rk} {LC.end_rk}\n")
f.write(f"*temps |débit massique (kg/s)\n") f.write(f"*temps |débit massique (kg/s)\n")
f.write(f"*---------++++++++++\n") f.write(f"*---------++++++++++\n")
@@ -729,7 +922,7 @@ class AdisTSwc(AdisTS):
data_tmp = {} data_tmp = {}
for file_bin in path_files: for file_bin in path_files:
key_pol = file_bin.split("/")[-1][0:-4] key_pol = os.path.basename(file_bin)[0:-4]
data_tmp[key_pol] = {} data_tmp[key_pol] = {}
with open(file_bin, 'rb') as f: with open(file_bin, 'rb') as f:
# header # header
+27 -7
View File
@@ -135,7 +135,7 @@ class Mage(CommandLineSolver):
) )
for line in lines: for line in lines:
rep_file.write(line.line) rep_file.write(line.line+"\n")
def _export_ST(self, study, repertory, qlog, name="0"): def _export_ST(self, study, repertory, qlog, name="0"):
files = [] files = []
@@ -717,7 +717,7 @@ class Mage(CommandLineSolver):
# RESULTS # # RESULTS #
########### ###########
def read_bin(self, study, repertory, results, qlog=None, name="0"): def read_bin(self, study, fname, results, qlog=None, name="0"):
return return
@timer @timer
@@ -728,7 +728,16 @@ class Mage(CommandLineSolver):
repertory=repertory, repertory=repertory,
name=name, name=name,
) )
self.read_bin(study, repertory, results, qlog, name=name) fname = os.path.join(repertory, f"{name}.BIN")
if not os.path.isfile(fname):
logger.info(f"Result file {name}.BIN does not exist")
return None
try:
self.read_bin(study, fname, results, qlog, name=name)
except Exception as e:
logger.error(f"Failed to read results")
logger_exception(e)
return None
return results return results
@@ -934,8 +943,7 @@ class Mage8(Mage):
########### ###########
@timer @timer
def read_bin(self, study, repertory, results, qlog=None, name="0"): def read_bin(self, study, fname, results, qlog=None, name="0"):
fname = os.path.join(repertory, f"{name}.BIN")
logger.info(f"read_bin: Start reading '{fname}' ...") logger.info(f"read_bin: Start reading '{fname}' ...")
with mage_file_open(fname, "r") as f: with mage_file_open(fname, "r") as f:
@@ -986,6 +994,7 @@ class Mage8(Mage):
reachs = [] reachs = []
iprofiles = {} iprofiles = {}
profile_len = []
reach_offset = {} reach_offset = {}
data = read_int(2*nb_reach) data = read_int(2*nb_reach)
@@ -1005,6 +1014,7 @@ class Mage8(Mage):
# Profile ID offset # Profile ID offset
reach_offset[r] = i1 reach_offset[r] = i1
profile_len.append(i2-i1+1)
logger.debug(f"read_bin: iprofiles = {iprofiles}") logger.debug(f"read_bin: iprofiles = {iprofiles}")
@@ -1033,6 +1043,15 @@ 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]
# check results and geometry consistency
for i, r in enumerate(reachs):
lp = len(r.profiles)
pl = profile_len[i]
if lp != pl:
logger.warning(f"geometry reach {i} has {lp} profiles")
logger.warning(f"results reach {i} has {pl} values")
return
ts = set() ts = set()
end = False end = False
while not end: while not end:
@@ -1042,7 +1061,7 @@ class Mage8(Mage):
f, dtype=np.byte, count=1)).decode() f, dtype=np.byte, count=1)).decode()
data = read_float(n) data = read_float(n)
logger.debug(f"read_bin: timestamp = {timestamp} sec") # logger.debug(f"read_bin: timestamp = {timestamp} sec")
ts.add(timestamp) ts.add(timestamp)
if key in ["Z", "Q"]: if key in ["Z", "Q"]:
@@ -1066,7 +1085,6 @@ class Mage8(Mage):
endline() endline()
end = newline().size <= 0 end = newline().size <= 0
logger.debug(reachs[0].profiles[0]._data)
results.set("timestamps", ts) results.set("timestamps", ts)
ts_list = sorted(ts) ts_list = sorted(ts)
logger.info(f"compute tab...") logger.info(f"compute tab...")
@@ -1265,6 +1283,8 @@ class Mage8(Mage):
name = study.name.replace(" ", "_") name = study.name.replace(" ", "_")
results = super(Mage8, self).results(study, repertory, qlog, name=name) results = super(Mage8, self).results(study, repertory, qlog, name=name)
if results is None:
return None
if with_gra: if with_gra:
self.read_gra(study, repertory, results, qlog, name=name) self.read_gra(study, repertory, results, qlog, name=name)
+330 -37
View File
@@ -1,5 +1,5 @@
# RubarBE.py -- Pamhyr # RubarBE.py -- Pamhyr
# Copyright (C) 2024 INRAE # Copyright (C) 2024-2025 INRAE
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@ import os
import logging import logging
import numpy as np import numpy as np
from tools import timer, trace, old_pamhyr_date_to_timestamp from tools import timer, trace, old_pamhyr_date_to_timestamp, logger_exception
from Solver.CommandLine import CommandLineSolver from Solver.CommandLine import CommandLineSolver
@@ -39,7 +39,7 @@ class Rubar3(CommandLineSolver):
self._type = "rubar3" self._type = "rubar3"
self._cmd_input = "" self._cmd_input = ""
self._cmd_solver = "@path @input -o @output" self._cmd_solver = "@path @args @input"
self._cmd_output = "" self._cmd_output = ""
@classmethod @classmethod
@@ -58,12 +58,12 @@ class Rubar3(CommandLineSolver):
("rubarbe_iovis", "n"), ("rubarbe_iovis", "n"),
("rubarbe_rep", "n"), ("rubarbe_rep", "n"),
("rubarbe_tinit", "000:00:00:00"), ("rubarbe_tinit", "000:00:00:00"),
("rubarbe_tmax", "999:99:99:00"), ("rubarbe_tmax", "000:01:00:00"), # 1 day
("rubarbe_tiopdt", "000:00:00:00"), ("rubarbe_tiopdt", "000:00:00:00"),
("rubarbe_dt", "5.0"), ("rubarbe_dt", "5.0"),
("rubarbe_ts", "999:99:99:00"), ("rubarbe_ts", "999:99:99:00"),
("rubarbe_dtsauv", "00:00:00:05"), ("rubarbe_dtsauv", "00:00:05:00"),
("rubarbe_psave", "00:00:00:05"), ("rubarbe_psave", "00:00:05:00"),
("rubarbe_fdeb1", "1"), ("rubarbe_fdeb1", "1"),
("rubarbe_fdeb2", "10"), ("rubarbe_fdeb2", "10"),
("rubarbe_fdeb3", "100"), ("rubarbe_fdeb3", "100"),
@@ -73,8 +73,9 @@ class Rubar3(CommandLineSolver):
("rubarbe_tf_4", "y"), ("rubarbe_tf_4", "y"),
("rubarbe_tf_5", "y"), ("rubarbe_tf_5", "y"),
("rubarbe_tf_6", "n"), ("rubarbe_tf_6", "n"),
("rubarbe_trased", "y"), ("rubarbe_trased", "n"),
("rubarbe_optfpc", "0"), ("rubarbe_optfpc", "0"),
# trased parameters
("rubarbe_ros", "2650.0"), ("rubarbe_ros", "2650.0"),
("rubarbe_dm", "0.1"), ("rubarbe_dm", "0.1"),
("rubarbe_segma", "1.0"), ("rubarbe_segma", "1.0"),
@@ -129,11 +130,11 @@ class Rubar3(CommandLineSolver):
def output_param(self): def output_param(self):
name = self._study.name name = self._study.name
return f"{name}" return f"profil.{name}"
def log_file(self): def log_file(self):
name = self._study.name name = self._study.name
return f"{name}" return f"geomac-i.{name}"
def export(self, study, repertory, qlog=None): def export(self, study, repertory, qlog=None):
self._study = study self._study = study
@@ -143,10 +144,12 @@ class Rubar3(CommandLineSolver):
self._export_ts(study, repertory, qlog, name=name) self._export_ts(study, repertory, qlog, name=name)
self._export_geomac_i(study, repertory, qlog, name=name) self._export_geomac_i(study, repertory, qlog, name=name)
self._export_mail(study, repertory, qlog, name=name) self._export_mail(study, repertory, qlog, name=name)
self._export_devers(study, repertory, qlog, name=name)
self._export_condin(study, repertory, qlog, name=name) self._export_condin(study, repertory, qlog, name=name)
self._export_stricklers(study, repertory, qlog, name=name) self._export_stricklers(study, repertory, qlog, name=name)
self._export_hydro(study, repertory, qlog, name=name) self._export_hydro(study, repertory, qlog, name=name)
self._export_condav(study, repertory, qlog, name=name) self._export_condav(study, repertory, qlog, name=name)
# self._export_abshyd(study, repertory, qlog, name=name)
return True return True
@@ -166,9 +169,8 @@ class Rubar3(CommandLineSolver):
it = iter(params) it = iter(params)
line = 0 param = next(it, None)
while line < 29: while param is not None:
param = next(it)
name = param.name name = param.name
value = param.value value = param.value
@@ -196,12 +198,12 @@ class Rubar3(CommandLineSolver):
v2 = next(it).value v2 = next(it).value
v3 = next(it).value v3 = next(it).value
f.write(f"{v2}{v3}") # f.write(f"{v2}{v3}")
# New line # New line
f.write(f"\n") f.write(f"\n")
line += 1 param = next(it, None)
def _export_ts(self, study, repertory, qlog, name="0"): def _export_ts(self, study, repertory, qlog, name="0"):
if qlog is not None: if qlog is not None:
@@ -277,19 +279,20 @@ class Rubar3(CommandLineSolver):
f.write(f"{ind:>4} {rk:>11.3f} {n_points:>4}\n") f.write(f"{ind:>4} {rk:>11.3f} {n_points:>4}\n")
for point in profile.points: station = profile.get_station()
for i, point in enumerate(profile.points):
label = point.name.lower() label = point.name.lower()
if label != "": if label != "":
if label[0] == "r": if label[0] == "r":
label = label[1].upper() label = label[1].upper()
else: else:
label = label[1].upper() label = " "
else: else:
label = " " label = " "
y = point.y y = station[i]
z = point.z z = point.z
dcs = 0.001 dcs = 1.0
scs = 1.0 scs = 1.0
tmcs = 0.0 tmcs = 0.0
@@ -329,6 +332,50 @@ class Rubar3(CommandLineSolver):
if ind % 3 != 0: if ind % 3 != 0:
f.write("\n") f.write("\n")
def _export_abshyd(self, study, repertory, qlog, name="0"):
if qlog is not None:
qlog.put("Export ABSHYD file")
with open(
os.path.join(
repertory, f"abshyd.{name}"
), "w+"
) as f:
reach_ind = 1
for edge in study.river.enable_edges():
reach = edge.reach
lm = len(reach) + 1
f.write(f"{lm:>13}\n")
ind = 1
for mail in reach.inter_profiles_rk():
f.write(f"{ind:>4} {mail:15.3f} {reach_ind:>4}\n")
ind += 1
reach_ind += 1
def _export_devers(self, study, repertory, qlog, name="0"):
if qlog is not None:
qlog.put("Export DEVERS file")
with open(
os.path.join(
repertory, f"devers.{name}"
), "w+"
) as f:
reach_ind = 1
for edge in study.river.enable_edges():
reach = edge.reach
lm = len(reach) + 1
f.write(f"{lm:>13}\n")
ind = 1
for mail in reach.inter_profiles_rk():
f.write(f"{ind:>4} {0.0:15.3f} {0.0:15.3f} {1.0:>15.3f}\n")
ind += 1
reach_ind += 1
def _export_stricklers(self, study, repertory, qlog, name="0"): def _export_stricklers(self, study, repertory, qlog, name="0"):
self._export_frot(study, repertory, qlog, name=name, version="") self._export_frot(study, repertory, qlog, name=name, version="")
self._export_frot(study, repertory, qlog, name=name, version="2") self._export_frot(study, repertory, qlog, name=name, version="2")
@@ -347,24 +394,57 @@ class Rubar3(CommandLineSolver):
lm = len(reach) + 1 lm = len(reach) + 1
f.write(f"{lm:>6}\n") f.write(f"{lm:>6}\n")
def get_stricklers_from_rk(rk): lst = list(filter(
return next( lambda f: f.is_full_defined(),
map( edge.frictions.frictions
lambda s: ( ))
s.begin_strickler.medium if version == "2"
else s.begin_strickler.minor rk_min = 9999999.9
), rk_max = -9999999.9
filter( coeff_min = -1.0
lambda f: rk in f, coeff_max = -1.0
edge.frictions.lst for s in lst: # TODO optimise ?
) if s.begin_rk > rk_max:
) rk_max = s.begin_rk
coeff_max = s.begin_strickler
if s.begin_rk < rk_min:
rk_min = s.begin_rk
coeff_min = s.begin_strickler
if s.end_rk > rk_max:
rk_max = s.end_rk
coeff_max = s.end_strickler
if s.end_rk < rk_min:
rk_min = s.end_rk
coeff_min = s.end_strickler
def get_stricklers_from_rk(rk, lst):
coeff = None
if rk > rk_max:
coeff = coeff_max
elif rk < rk_min:
coeff = coeff_min
else:
for s in lst:
if (rk >= s.begin_rk and rk <= s.end_rk or
rk <= s.begin_rk and rk >= s.end_rk):
coeff = s.begin_strickler # TODO: inerpolate
break
# TODO interpolation if rk is not in frictons
if coeff is None:
logger.error(
"Study frictions are not fully defined"
) )
return None
return coeff.medium if version == "2" else coeff.minor
ind = 1 ind = 1
for mail in edge.reach.inter_profiles_rk(): for mail in edge.reach.inter_profiles_rk():
coef = get_stricklers_from_rk(mail) coef = get_stricklers_from_rk(mail, lst)
if coef is not None:
f.write(f"{ind:>6} {coef:>12.5f}") f.write(f"{ind:>6} {coef:>12.5f}")
ind += 1 ind += 1
@@ -382,7 +462,7 @@ class Rubar3(CommandLineSolver):
for edge in study.river.enable_edges(): for edge in study.river.enable_edges():
reach = edge.reach reach = edge.reach
f.write(f"0.0\n") f.write(f"{0.0}\n")
ics = study.river.initial_conditions.get(edge) ics = study.river.initial_conditions.get(edge)
data = self._export_condin_init_data(ics) data = self._export_condin_init_data(ics)
@@ -391,7 +471,9 @@ class Rubar3(CommandLineSolver):
first = profiles[0] first = profiles[0]
last = profiles[-1] last = profiles[-1]
# if first not in data or last not in data:
if first.rk not in data or last.rk not in data: if first.rk not in data or last.rk not in data:
print(data)
logger.error( logger.error(
"Study initial condition is not fully defined" "Study initial condition is not fully defined"
) )
@@ -444,10 +526,10 @@ class Rubar3(CommandLineSolver):
z = data[profile.rk][0] z = data[profile.rk][0]
q = data[profile.rk][1] q = data[profile.rk][1]
# height = z - profile.z_min() height = z - profile.z_min()
speed = profile.speed(q, z) speed = profile.speed(q, z)
return z, speed return height, speed
def _export_hydro(self, study, repertory, qlog, name="0"): def _export_hydro(self, study, repertory, qlog, name="0"):
if qlog is not None: if qlog is not None:
@@ -470,7 +552,7 @@ class Rubar3(CommandLineSolver):
for bc in bcs: for bc in bcs:
f.write(f"{len(bc)}\n") f.write(f"{len(bc)}\n")
for d0, d1 in bc.data: for d0, d1 in bc.data:
f.write(f"{d0} {d1}\n") f.write(f"{d1} {d0}\n")
def _export_condav(self, study, repertory, qlog, name="0"): def _export_condav(self, study, repertory, qlog, name="0"):
if qlog is not None: if qlog is not None:
@@ -492,9 +574,220 @@ class Rubar3(CommandLineSolver):
for bc in bcs: for bc in bcs:
f.write(f"{len(bc)}\n") f.write(f"{len(bc)}\n")
if bc.bctype == "ZD":
for d0, d1 in bc.data:
f.write(f"{d1} {d0}\n")
else:
for d0, d1 in bc.data: for d0, d1 in bc.data:
f.write(f"{d0} {d1}\n") f.write(f"{d0} {d1}\n")
def read_profil(self, study, fname, results, qlog=None, name="0"):
logger.info(f"read: profil.{name}")
with open(fname, "r") as f:
reachs = []
for i, edge in enumerate(study.river.enable_edges()):
reach = edge.reach
# Add results reach to reach list
res_reach = results.river.add(i)
reachs.append((res_reach, len(reach) + 1))
def read_data_line(f):
line = f.readline().split()
if len(line) > 3:
return tuple(map(float, line))
else:
return (0.0, 0.0, 0.0, 0.0)
def set_and_compute_limites(reach, ind, z, q, s):
reach.set(ind, timestamp, "Z", z)
reach.set(ind, timestamp, "Q", q)
reach.set(ind, timestamp, "V", s)
profile = reach.profile(ind)
limits = profile.geometry.get_water_limits(z)
reach.set(
ind, timestamp, "water_limits", limits
)
ts = set()
timestamp = next(filter(
lambda p: p.name == 'rubarbe_tinit',
study.river.get_params(self._type).parameters
)).value
if timestamp.count(':') == 3:
timestamp = old_pamhyr_date_to_timestamp(timestamp)
ts.add(timestamp)
# add initial condition
for r, edge in enumerate(study.river.enable_edges()):
reach = edge.reach
ics = study.river.initial_conditions.get(edge)
q = ics.get_discharge()
z = ics.get_elevation()
k = 0
for i, j in zip(z, q):
v = reach.profiles[k].speed(i, j)
set_and_compute_limites(reachs[r][0], k, i, j, v)
k += 1
# start read
end = False
while True:
line = f.readline()
if line == "":
results.set("timestamps", ts)
return
timestamp = float(line)
ts.add(timestamp)
logger.info(f"read: profil.{name}: timestamp = {timestamp}")
for reach, lm in reachs:
# First profile
h, s, q, z = read_data_line(f)
set_and_compute_limites(reach, 0, z+h, q, s)
# For each profile
ind = 1
ph, ps, pq, pz = read_data_line(f)
while ind < lm - 2:
h, s, q, z = read_data_line(f)
set_and_compute_limites(
reach, ind,
(pz + z + ph + h) / 2,
(pq + q) / 2,
(ps + s) / 2
)
ph = h
ps = s
pq = q
pz = z
ind += 1
# Last profile
h, s, q, z = read_data_line(f)
set_and_compute_limites(reach, ind, z+h, q, s)
@timer
def write_bin(self, study, fname, results, qlog=None, name="0"):
logger.info(f"write_bin: Start writing '{fname}' ...")
with open(fname, "wb") as f:
def newline(j): return np.asarray([j], dtype=np.int32).tofile(f)
def endline(j): return np.asarray([j], dtype=np.int32).tofile(f)
def write_int(i):
newline(len(i)*4)
np.array(i, dtype=np.int32).tofile(f)
endline(len(i)*4)
def write_float(i):
newline(len(i)*4)
np.array(i, dtype=np.float32).tofile(f)
endline(len(i)*4)
def write_float64(i):
newline(len(i)*8)
np.array(i, dtype=np.float64).tofile(f)
endline(len(i)*8)
def write_float64(i):
newline(len(i)*8)
np.array(i, dtype=np.float64).tofile(f)
endline(len(i)*8)
def write_data(npts, t, a, val):
newline(npts*4 + 13)
np.array(npts, dtype=np.int32).tofile(f)
np.array(t, dtype=np.float64).tofile(f)
np.array(bytearray(a.encode()), dtype=np.byte).tofile(f)
np.array(val, dtype=np.float32).tofile(f)
endline(npts*4 + 13)
ts_list = sorted(results.get("timestamps"))
profiles = []
for r in results.river.reachs:
profiles += r.profiles
# Meta data (1st line)
nb_reach = len(results.river)
nb_profile = len(profiles)
write_int([nb_reach, nb_profile, "82"])
# Reach information (2nd line)
is1 = []
is2 = []
ltmp = []
i = 1
for r in results.river.reachs:
j = i+len(r)-1
ltmp += [i, j]
is1.append(i)
is2.append(j)
i += i+len(r)
write_int(ltmp)
# X (3rd line)
rk = []
for r in results.river.reachs:
rk += r.geometry.get_rk()
write_float(rk)
# Z and Y (4th line)
# ltmp = []
# for r in results.river.reachs:
# for p in r.prifiles:
# ltpm.append(p.rk)
write_float(3*nb_profile*[0.0])
# Data
for timestamp in ts_list:
q = list(
map(
lambda p: p.get_ts_key(timestamp, "Q"),
profiles
)
)
write_data(nb_profile, timestamp, "Q", q)
z = list(
map(
lambda p: p.get_ts_key(timestamp, "Z"),
profiles
)
)
write_data(nb_profile, timestamp, "Z", z)
logger.info(f"write_bin: ... end with {len(ts_list)} timestamps")
@timer
def results(self, study, repertory, qlog=None, name="0"):
results = Results(
study=study,
solver=self,
repertory=repertory,
name=name,
)
results_file = f"profil.{name}"
fname = os.path.join(repertory, results_file)
if not os.path.isfile(fname):
logger.warning(f"Result file {results_file} does not exist")
return None
try:
self.read_profil(study, fname, results, qlog, name=name)
except Exception as e:
logger.error(f"Failed to read results")
logger_exception(e)
return None
fname = os.path.join(repertory, f"{name}.BIN")
self.write_bin(study, fname, results, qlog, name)
return results
class RubarBE(Rubar3): class RubarBE(Rubar3):
_type = "rubarbe" _type = "rubarbe"
@@ -505,5 +798,5 @@ class RubarBE(Rubar3):
self._type = "rubarbe" self._type = "rubarbe"
self._cmd_input = "" self._cmd_input = ""
self._cmd_solver = "@path @input -o @output" self._cmd_solver = "@path @args @input"
self._cmd_output = "" self._cmd_output = ""
+2 -2
View File
@@ -34,7 +34,7 @@ solver_long_name = {
# "mage_fake7": "Mage fake v7", # "mage_fake7": "Mage fake v7",
"adistswc": "Adis-TS_WC", "adistswc": "Adis-TS_WC",
# "rubarbe": "RubarBE", # "rubarbe": "RubarBE",
# "rubar3": "Rubar3", "rubar3": "Rubar3",
} }
solver_type_list = { solver_type_list = {
@@ -44,5 +44,5 @@ solver_type_list = {
# "mage_fake7": MageFake7, # "mage_fake7": MageFake7,
"adistswc": AdisTSwc, "adistswc": AdisTSwc,
# "rubarbe": RubarBE, # "rubarbe": RubarBE,
# "rubar3": Rubar3, "rubar3": Rubar3,
} }
+1 -1
View File
@@ -55,7 +55,7 @@ class AboutWindow(PamhyrDialog):
label = self.get_label_text("label_version") label = self.get_label_text("label_version")
label = label.replace("@version", version) label = label.replace("@version", version)
label = label.replace("@codename", "(Adis-TS)") label = label.replace("@codename", "(Le Coz)")
self.set_label_text("label_version", label) self.set_label_text("label_version", label)
# Authors # Authors
+23
View File
@@ -211,3 +211,26 @@ class TableModel(PamhyrTableModel):
) )
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.update() self.update()
def read_from_file(self, file_name, bctype):
logger.debug(f"Import boundary conditions from {file_name}")
data0 = []
data1 = []
if bctype == "ZD":
mult = 1
else:
mult = 60
with open(file_name, encoding="utf-8") as bc_file:
for line in bc_file:
if not (line.startswith("#") or
line.startswith("*") or
line.startswith("$")):
line = line.split()
if bctype == "ZD":
data0.append(float(line[0]))
else:
data0.append(old_pamhyr_date_to_timestamp(line[0]))
data1.append(line[1])
self.replace_data(data0, data1)
+35 -4
View File
@@ -30,10 +30,10 @@ from PyQt5.QtGui import (
QKeySequence, QKeySequence,
) )
from PyQt5 import QtCore from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, QCoreApplication, Qt, QVariant, QAbstractTableModel, QCoreApplication,
pyqtSlot, pyqtSignal, pyqtSlot, pyqtSignal, QSettings,
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
@@ -198,6 +198,8 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort) self.find(QAction, "action_sort").triggered.connect(self.sort)
self.find(QAction, "action_import").triggered\
.connect(self.import_from_file)
self.find(QAction, "action_generate_uniform").triggered.connect( self.find(QAction, "action_generate_uniform").triggered.connect(
self.generate_uniform self.generate_uniform
) )
@@ -293,6 +295,31 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self._table.sort(False) self._table.sort(False)
self.plot.update() self.plot.update()
def import_from_file(self):
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
if self._data.bctype == "TD":
file_types = [self._trad["file_hyd"]]
if self._data.bctype == "TZ":
file_types = [self._trad["file_lim"]]
if self._data.bctype == "ZD":
file_types = [self._trad["file_ava"]]
file_types.append(self._trad["file_all"])
file_name, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
)
if file_name != "":
self._table.read_from_file(file_name, self._data.bctype)
def move_up(self): def move_up(self):
row = self.index_selected_row() row = self.index_selected_row()
self._table.move_up(row) self._table.move_up(row)
@@ -372,8 +399,12 @@ class EditBoundaryConditionWindow(PamhyrWindow):
if strickler is None: if strickler is None:
strickler = 25.0 strickler = 25.0
height = [(i)*(z_max-z_min)/50 for i in range(51)] height = [(i)*(z_max-z_min)/50 for i in range(51)]
q = [((profile.wet_width(z_min + h) * 0.8) * strickler # q = [((profile.wet_width(z_min + h) * 0.8) * strickler
* (h ** (5/3)) * (abs(self.slope_value) ** (0.5))) # * (h ** (5/3)) * (abs(self.slope_value) ** (0.5)))
# for h in height]
q = [profile.wet_area(z_min + h) * strickler
* (profile.wet_radius(z_min + h) ** (2/3))
* (abs(self.slope_value) ** (0.5))
for h in height] for h in height]
for i in range(len(height)): for i in range(len(height)):
height[i] += z_min height[i] += z_min
@@ -35,6 +35,17 @@ class BCETranslate(BCTranslate):
self._dict["Boundary Condition Options"] = _translate( self._dict["Boundary Condition Options"] = _translate(
"BoundaryCondition", "Boundary Condition Options") "BoundaryCondition", "Boundary Condition Options")
self._dict["open_file"] = _translate(
"BoundaryCondition", "Open a file")
self._dict["file_hyd"] = _translate(
"BoundaryCondition", "Mage hydrograph file (*.HYD)")
self._dict["file_lim"] = _translate(
"BoundaryCondition", "Mage limnigraph file (*.LIM)")
self._dict["file_ava"] = _translate(
"BoundaryCondition", "Mage rating curve file (*.AVA)")
self._dict["file_all"] = _translate(
"BoundaryCondition", "All files (*)")
self._sub_dict["table_headers"] = { self._sub_dict["table_headers"] = {
"x": _translate("BoundaryCondition", "X"), "x": _translate("BoundaryCondition", "X"),
"y": _translate("BoundaryCondition", "Y"), "y": _translate("BoundaryCondition", "Y"),
@@ -41,7 +41,7 @@ from PyQt5.QtWidgets import (
) )
from View.BoundaryConditionsAdisTS.Edit.UndoCommand import ( from View.BoundaryConditionsAdisTS.Edit.UndoCommand import (
AddCommand, DelCommand, SetDataCommand, AddCommand, DelCommand, SetDataCommand, PasteCommand, SortCommand,
) )
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
@@ -118,3 +118,40 @@ class TableModel(PamhyrTableModel):
) )
self.endRemoveRows() self.endRemoveRows()
def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit()
self._undo.push(
SortCommand(
self._data, _reverse
)
)
self.layoutAboutToBeChanged.emit()
self.update()
def paste(self, row, header, data):
if len(data) == 0:
return
self.layoutAboutToBeChanged.emit()
self._undo.push(
PasteCommand(
self._data, row,
list(
map(
lambda d: self._data.new_from_data(header, d),
data
)
)
)
)
self.layoutAboutToBeChanged.emit()
self.update()
def update(self):
# self.auto_sort()
self.layoutChanged.emit()
@@ -38,29 +38,14 @@ class SetDataCommand(QUndoCommand):
self._data = data self._data = data
self._index = index self._index = index
self._column = column self._column = column
self._old = self._data._data[self._index][self._column] self._old = self._data.get_i(self._index)[self._column]
_type = self._data._types[self._column] self._new = new_value
self._new = _type(new_value)
def undo(self): def undo(self):
if self._column == 0: self._data._set_i_c_v(self._index, self._column, self._old)
self._data._data[self._index] = (
self._old, self._data._data[self._index][1]
)
else:
self._data._data[self._index] = (
self._data._data[self._index][0], self._old
)
def redo(self): def redo(self):
if self._column == 0: self._data._set_i_c_v(self._index, self._column, self._new)
self._data._data[self._index] = (
self._new, self._data._data[self._index][1]
)
else:
self._data._data[self._index] = (
self._data._data[self._index][0], self._new
)
class AddCommand(QUndoCommand): class AddCommand(QUndoCommand):
@@ -76,11 +61,11 @@ class AddCommand(QUndoCommand):
def redo(self): def redo(self):
if self._new is None: if self._new is None:
self._new = self._data._data.insert(self._index, ( self._new = self._data.insert(self._index, (
self._data._types[0](0), self._data._types[1](0.0) self._data._types[0](0), self._data._types[1](0.0)
)) ))
else: else:
self._data._data.insert(self._index, self._new) self._data.insert(self._index, self._new)
class DelCommand(QUndoCommand): class DelCommand(QUndoCommand):
@@ -97,8 +82,57 @@ class DelCommand(QUndoCommand):
def undo(self): def undo(self):
for row, el in self._bc: for row, el in self._bc:
self._data._data.insert(row, el) self._data.insert(row, el)
def redo(self): def redo(self):
for row in self._rows: self._data.delete_i(self._rows)
del self._data._data[row]
class PasteCommand(QUndoCommand):
def __init__(self, data, row, bcs):
QUndoCommand.__init__(self)
self._data = data
self._row = row
self._bcs = bcs
self._bcs.reverse()
def undo(self):
self._data.delete_i(
range(self._row, self._row + len(self._bcs))
)
def redo(self):
for bc in self._bcs:
self._data.insert(self._row, bc)
class SortCommand(QUndoCommand):
def __init__(self, data, _reverse):
QUndoCommand.__init__(self)
self._data = data
self._reverse = _reverse
self._old = self._data.data
self._indexes = None
def undo(self):
ll = self._data.data
self._data.sort(
key=lambda x: self._indexes[ll.index(x)]
)
def redo(self):
self._data.sort(
_reverse=self._reverse,
key=lambda x: x[0]
)
if self._indexes is None:
self._indexes = list(
map(
lambda p: self._old.index(p),
self._data.data
)
)
self._old = None
@@ -143,7 +143,7 @@ class EditBoundaryConditionWindow(PamhyrWindow):
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self._table.dataChanged.connect(self.update) self._table.dataChanged.connect(self.update)
def update(self): def update(self):
@@ -200,6 +200,22 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self.copyTableIntoClipboard(table) self.copyTableIntoClipboard(table)
def _paste(self):
header, data = self.parseClipboardTable()
logger.debug(f"paste: h:{header}, d:{data}")
if len(data) == 0:
return
row = 0
rows = self.index_selected_rows()
if len(rows) != 0:
row = rows[0]
self._table.paste(row, header, data)
self.plot.update()
def _undo(self): def _undo(self):
self._table.undo() self._table.undo()
self.plot.update() self.plot.update()
+10 -17
View File
@@ -119,17 +119,10 @@ class TableModel(PamhyrTableModel):
super(TableModel, self).__init__(trad=trad, **kwargs) super(TableModel, self).__init__(trad=trad, **kwargs)
def _setup_lst(self):
self._lst = self._bc_list.lst
def rowCount(self, parent): def rowCount(self, parent):
return len(self._lst) return len(self._bc_list)
def data(self, index, role): def data(self, index, role):
if len(self._lst) != 0:
data = self._lst
else:
data = []
if role != Qt.ItemDataRole.DisplayRole: if role != Qt.ItemDataRole.DisplayRole:
return QVariant() return QVariant()
@@ -138,12 +131,12 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
if self._headers[column] == "type": if self._headers[column] == "type":
n = data[row].type n = self._bc_list.get(row).type
if n is None or n == "": if n is None or n == "":
return self._trad["not_associated"] return self._trad["not_associated"]
return n return n
elif self._headers[column] == "node": elif self._headers[column] == "node":
n = data[row].node n = self._bc_list.get(row).node
if n is None: if n is None:
return self._trad["not_associated"] return self._trad["not_associated"]
tmp = next(filter(lambda x: x.id == n, self._data._nodes), None) tmp = next(filter(lambda x: x.id == n, self._data._nodes), None)
@@ -152,7 +145,7 @@ class TableModel(PamhyrTableModel):
else: else:
return self._trad["not_associated"] return self._trad["not_associated"]
elif self._headers[column] == "pol": elif self._headers[column] == "pol":
n = data[row].pollutant n = self._bc_list.get(row).pollutant
if n is None or n == "not_associated" or n == "": if n is None or n == "not_associated" or n == "":
return self._trad["not_associated"] return self._trad["not_associated"]
tmp = next(filter(lambda x: x.id == n, tmp = next(filter(lambda x: x.id == n,
@@ -177,20 +170,20 @@ class TableModel(PamhyrTableModel):
if self._headers[column] == "type": if self._headers[column] == "type":
self._undo.push( self._undo.push(
SetTypeCommand( SetTypeCommand(
self._lst, row, value self._bc_list, row, value
) )
) )
elif self._headers[column] == "node": elif self._headers[column] == "node":
self._undo.push( self._undo.push(
SetNodeCommand( SetNodeCommand(
self._lst, row, self._data.node(value) self._bc_list, row, self._data.node(value)
) )
) )
elif self._headers[column] == "pol": elif self._headers[column] == "pol":
if value == self._trad["not_associated"]: if value == self._trad["not_associated"]:
self._undo.push( self._undo.push(
SetPolCommand( SetPolCommand(
self._lst, row, None self._bc_list, row, None
) )
) )
else: else:
@@ -199,7 +192,7 @@ class TableModel(PamhyrTableModel):
) )
self._undo.push( self._undo.push(
SetPolCommand( SetPolCommand(
self._lst, row, pol.id self._bc_list, row, pol.id
) )
) )
except Exception as e: except Exception as e:
@@ -214,7 +207,7 @@ class TableModel(PamhyrTableModel):
self._undo.push( self._undo.push(
AddCommand( AddCommand(
self._pollutant, self._bc_list, self._lst, row self._pollutant, self._bc_list, row
) )
) )
@@ -226,7 +219,7 @@ class TableModel(PamhyrTableModel):
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._lst, rows self._bc_list, rows
) )
) )
@@ -35,14 +35,14 @@ class SetNodeCommand(QUndoCommand):
self._bcs = bcs self._bcs = bcs
self._index = index self._index = index
self._old = self._bcs[self._index].node self._old = self._bcs.get(self._index).node
self._new = node.id self._new = node.id
def undo(self): def undo(self):
self._bcs[self._index].node = self._old self._bcs.get(self._index).node = self._old
def redo(self): def redo(self):
self._bcs[self._index].node = self._new self._bcs.get(self._index).node = self._new
class SetTypeCommand(QUndoCommand): class SetTypeCommand(QUndoCommand):
@@ -52,14 +52,14 @@ class SetTypeCommand(QUndoCommand):
self._bcs = bcs self._bcs = bcs
self._index = index self._index = index
self._type = _type self._type = _type
self._old = self._bcs[self._index].type self._old = self._bcs.get(self._index).type
self._new = self._type self._new = self._type
def undo(self): def undo(self):
self._bcs[self._index].type = self._old self._bcs.get(self._index).type = self._old
def redo(self): def redo(self):
self._bcs[self._index].type = self._new self._bcs.get(self._index).type = self._new
class SetPolCommand(QUndoCommand): class SetPolCommand(QUndoCommand):
@@ -68,32 +68,31 @@ class SetPolCommand(QUndoCommand):
self._bcs = bcs self._bcs = bcs
self._index = index self._index = index
self._old = self._bcs[self._index].pollutant self._old = self._bcs.get(self._index).pollutant
self._new = pollutant self._new = pollutant
def undo(self): def undo(self):
self._bcs[self._index].pollutant = self._old self._bcs.get(self._index).pollutant = self._old
def redo(self): def redo(self):
self._bcs[self._index].pollutant = self._new self._bcs.get(self._index).pollutant = self._new
class AddCommand(QUndoCommand): class AddCommand(QUndoCommand):
def __init__(self, pollutant, bcs_list, bcs, index): def __init__(self, pollutant, bcs, index):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._bcs = bcs self._bcs = bcs
self._bc_list = bcs_list
self._pollutant = pollutant self._pollutant = pollutant
self._index = index self._index = index
self._new = None self._new = None
def undo(self): def undo(self):
del self._bcs[self._index] self._bcs.delete_i([self._index])
def redo(self): def redo(self):
if self._new is None: if self._new is None:
self._new = self._bc_list.new(self._index, self._pollutant) self._new = self._bcs.new(self._index, self._pollutant)
else: else:
self._bcs.insert(self._index, self._new) self._bcs.insert(self._index, self._new)
@@ -107,7 +106,7 @@ class DelCommand(QUndoCommand):
self._bc = [] self._bc = []
for row in rows: for row in rows:
self._bc.append((row, self._bcs[row])) self._bc.append((row, self._bcs.get(row)))
self._bc.sort() self._bc.sort()
def undo(self): def undo(self):
@@ -115,5 +114,4 @@ class DelCommand(QUndoCommand):
self._bcs.insert(row, el) self._bcs.insert(row, el)
def redo(self): def redo(self):
for row in self._rows: self._bcs.delete_i(self._rows)
del self._bcs[row]
+4 -1
View File
@@ -180,7 +180,10 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
def edit(self): def edit(self):
rows = self.index_selected_rows() rows = self.index_selected_rows()
for row in rows: for row in rows:
data = self._bcs.lst[row] data = self._bcs.get(row)
if data.node is None:
continue
if self.sub_window_exists( if self.sub_window_exists(
EditBoundaryConditionWindow, EditBoundaryConditionWindow,
+5 -6
View File
@@ -130,29 +130,29 @@ class D90TableModel(PamhyrTableModel):
row = index.row() row = index.row()
column = index.column() column = index.column()
if self._headers[column] is "name": if self._headers[column] == "name":
n = self._lst[row].name n = self._lst[row].name
if n is None or n == "": if n is None or n == "":
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "reach": elif self._headers[column] == "reach":
n = self._lst[row].reach n = self._lst[row].reach
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return next(filter( return next(filter(
lambda edge: edge.id == n, self._river.edges() lambda edge: edge.id == n, self._river.edges()
)).name )).name
elif self._headers[column] is "start_rk": elif self._headers[column] == "start_rk":
n = self._lst[row].start_rk n = self._lst[row].start_rk
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "end_rk": elif self._headers[column] == "end_rk":
n = self._lst[row].end_rk n = self._lst[row].end_rk
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "d90": elif self._headers[column] == "d90":
n = self._lst[row].d90 n = self._lst[row].d90
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
@@ -175,7 +175,6 @@ class D90TableModel(PamhyrTableModel):
) )
) )
elif self._headers[column] == "reach": elif self._headers[column] == "reach":
print(self._river.edge(value).id)
self._undo.push( self._undo.push(
SetCommandSpec( SetCommandSpec(
self._lst, row, self._headers[column], self._lst, row, self._headers[column],
-2
View File
@@ -279,9 +279,7 @@ class D90AdisTSWindow(PamhyrWindow):
self._table_spec.add(rows[0]) self._table_spec.add(rows[0])
def delete(self): def delete(self):
print("del")
rows = self.index_selected_rows() rows = self.index_selected_rows()
if len(rows) == 0: if len(rows) == 0:
print("len 0")
return return
self._table_spec.delete(rows) self._table_spec.delete(rows)
+7 -7
View File
@@ -132,39 +132,39 @@ class DIFTableModel(PamhyrTableModel):
row = index.row() row = index.row()
column = index.column() column = index.column()
if self._headers[column] is "method": if self._headers[column] == "method":
n = self._lst[row].method n = self._lst[row].method
if n is None or n == "": if n is None or n == "":
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "reach": elif self._headers[column] == "reach":
n = self._lst[row].reach n = self._lst[row].reach
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return next(filter( return next(filter(
lambda edge: edge.id == n, self._river.edges() lambda edge: edge.id == n, self._river.edges()
)).name )).name
elif self._headers[column] is "start_rk": elif self._headers[column] == "start_rk":
n = self._lst[row].start_rk n = self._lst[row].start_rk
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "end_rk": elif self._headers[column] == "end_rk":
n = self._lst[row].end_rk n = self._lst[row].end_rk
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "dif": elif self._headers[column] == "dif":
n = self._lst[row].dif n = self._lst[row].dif
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "b": elif self._headers[column] == "b":
n = self._lst[row].b n = self._lst[row].b
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "c": elif self._headers[column] == "c":
n = self._lst[row].c n = self._lst[row].c
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
+4 -4
View File
@@ -55,22 +55,22 @@ class DIFTableDefaultModel(PamhyrTableModel):
row = index.row() row = index.row()
column = index.column() column = index.column()
if self._headers[column] is "method": if self._headers[column] == "method":
n = self._data[row].method n = self._data[row].method
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "dif": elif self._headers[column] == "dif":
n = self._data[row].dif n = self._data[row].dif
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "b": elif self._headers[column] == "b":
n = self._data[row].b n = self._data[row].b
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "c": elif self._headers[column] == "c":
n = self._data[row].c n = self._data[row].c
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
+43 -2
View File
@@ -38,7 +38,7 @@ from View.Frictions.UndoCommand import (
SetNameCommand, SetBeginCommand, SetEndCommand, SetNameCommand, SetBeginCommand, SetEndCommand,
SetBeginStricklerCommand, SetEndStricklerCommand, SetBeginStricklerCommand, SetEndStricklerCommand,
AddCommand, DelCommand, SortCommand, AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand, MoveCommand, PasteCommand, DuplicateCommand, ReplaceDataCommand,
) )
from View.Tools.PamhyrTable import PamhyrTableModel from View.Tools.PamhyrTable import PamhyrTableModel
@@ -99,7 +99,7 @@ class ComboBoxDelegate(QItemDelegate):
self.commitData.emit(self.sender()) self.commitData.emit(self.sender())
class TableModel(PamhyrTableModel): class FrictionTableModel(PamhyrTableModel):
def _setup_lst(self): def _setup_lst(self):
self._lst = self._data.frictions self._lst = self._data.frictions
self._study = self._opt_data self._study = self._opt_data
@@ -199,6 +199,18 @@ class TableModel(PamhyrTableModel):
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def replace_data(self, new_data):
self.layoutAboutToBeChanged.emit()
self._undo.push(
ReplaceDataCommand(
self._lst, new_data
)
)
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def sort(self, _reverse, parent=QModelIndex()): def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
@@ -252,3 +264,32 @@ class TableModel(PamhyrTableModel):
def redo(self): def redo(self):
self._undo.redo() self._undo.redo()
self.layoutChanged.emit() self.layoutChanged.emit()
def read_from_file(self, file_name):
reach_id = self._study.river.enable_edges().index(self._data) + 1
logger.debug(f"Import frictions from {file_name}")
data = []
with open(file_name, encoding="utf-8") as rug_file:
for line in rug_file:
if line.upper().startswith("K"):
if int(line[1:4]) == reach_id:
data.append(line[4:].split())
new_data = []
for d in data:
new = None
minor = float(d[2])
medium = float(d[3])
for s in self._study.river.stricklers.stricklers:
if s.minor == minor and s.medium == medium:
new = s
break
if new is None:
new = self._study.river.stricklers.new(len(
self._study.river.stricklers))
new.minor = minor
new.medium = medium
new_data.append([self._data, float(d[0]), float(d[1]), new, new])
self.replace_data(new_data)
+36
View File
@@ -143,6 +143,42 @@ class AddCommand(QUndoCommand):
self._frictions.insert(self._index, self._new) self._frictions.insert(self._index, self._new)
class ReplaceDataCommand(QUndoCommand):
def __init__(self, frictions, new_data):
QUndoCommand.__init__(self)
self._frictions = frictions
self._new_data = new_data
self._old_rows = list(range(len(frictions)))
self._new_rows = list(range(len(new_data)))
self._new = []
self._old_friction = []
for row in self._old_rows:
self._old_friction.append((row, self._frictions.lst[row]))
def undo(self):
self._frictions.delete_i(self._new_rows)
for row, el in self._old_friction:
self._frictions.insert(row, el)
def redo(self):
if len(self._new) == 0:
self._frictions.delete_i(self._old_rows)
for row in self._new_rows:
new = self._frictions.new(row)
d = self._new_data[row]
new.edge = d[0]
new.begin_rk = d[1]
new.end_rk = d[2]
new.begin_strickler = d[3]
new.end_strickler = d[4]
self._new.append((row, new))
else:
for row, el in self._new:
self._frictions.insert(row, el)
class DelCommand(QUndoCommand): class DelCommand(QUndoCommand):
def __init__(self, frictions, rows): def __init__(self, frictions, rows):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
+25 -3
View File
@@ -26,10 +26,11 @@ from PyQt5.QtGui import (
QKeySequence, QKeySequence,
) )
from PyQt5 import QtWidgets
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot, QCoreApplication, QModelIndex, pyqtSlot,
QRect, QRect, QSettings,
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
@@ -46,7 +47,7 @@ from View.Frictions.UndoCommand import (
) )
from View.Frictions.Table import ( from View.Frictions.Table import (
TableModel, ComboBoxDelegate FrictionTableModel, ComboBoxDelegate
) )
from View.Tools.Plot.PamhyrCanvas import MplCanvas from View.Tools.Plot.PamhyrCanvas import MplCanvas
@@ -106,7 +107,7 @@ class FrictionsWindow(PamhyrWindow):
) )
table = self.find(QTableView, f"tableView") table = self.find(QTableView, f"tableView")
self._table = TableModel( self._table = FrictionTableModel(
table_view=table, table_view=table,
table_headers=self._trad.get_dict("table_headers"), table_headers=self._trad.get_dict("table_headers"),
editable_headers=[ editable_headers=[
@@ -157,6 +158,8 @@ class FrictionsWindow(PamhyrWindow):
self.plot_2.draw() self.plot_2.draw()
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_import").triggered\
.connect(self.import_from_file)
self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort) self.find(QAction, "action_sort").triggered.connect(self.sort)
@@ -265,3 +268,22 @@ class FrictionsWindow(PamhyrWindow):
parent=self parent=self
) )
strick.show() strick.show()
def import_from_file(self):
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
file_types = [self._trad["file_rug"], self._trad["file_all"]]
file_name, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
)
if file_name != "":
self._table.read_from_file(file_name)
+4
View File
@@ -38,6 +38,10 @@ class FrictionsTranslate(MainTranslate):
self._dict["Edit frictions"] = _translate( self._dict["Edit frictions"] = _translate(
"Frictions", "Edit frictions" "Frictions", "Edit frictions"
) )
self._dict["file_rug"] = _translate(
"Frictions", "Mage initial frictions file (*.RUG *.rug)")
self._dict["file_all"] = _translate(
"Frictions", "All files (*)")
self._sub_dict["table_headers"] = { self._sub_dict["table_headers"] = {
# "name": self._dict["name"], # "name": self._dict["name"],
+16 -6
View File
@@ -23,12 +23,12 @@ from PyQt5.QtGui import (
) )
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, Qt, QVariant, QAbstractTableModel, QItemSelectionModel,
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialogButtonBox, QComboBox, QUndoStack, QShortcut, QDialogButtonBox, QComboBox, QUndoStack, QShortcut,
QDoubleSpinBox, QDoubleSpinBox, QRadioButton,
) )
@@ -55,7 +55,7 @@ class MeshingDialog(PamhyrDialog):
self._space_step = 50.0 self._space_step = 50.0
self._lplan = False self._lplan = False
self._lm = "3" self._lm = "3"
self._linear = False self._linear = True
self._begin_cs = -1 self._begin_cs = -1
self._end_cs = -1 self._end_cs = -1
self._begin_dir = "un" self._begin_dir = "un"
@@ -71,7 +71,7 @@ class MeshingDialog(PamhyrDialog):
self._origin = self._reach.profile(0) self._origin = self._reach.profile(0)
self._init_default_values_profiles() self._init_default_values_profiles()
self._init_default_values_guidelines() # self._init_default_values_guidelines()
self.set_double_spin_box( self.set_double_spin_box(
"doubleSpinBox_space_step", "doubleSpinBox_space_step",
@@ -83,14 +83,24 @@ class MeshingDialog(PamhyrDialog):
else: else:
self.set_radio_button("radioButton_spline", True) self.set_radio_button("radioButton_spline", True)
self.find(QRadioButton, "radioButton_spline").setEnabled(False)
self.find(QRadioButton, "radioButton_linear").setEnabled(False)
def _init_default_values_profiles(self): def _init_default_values_profiles(self):
profiles = self.profiles profiles = self.profiles
self.combobox_add_items("comboBox_begin_rk", profiles) self.combobox_add_items("comboBox_begin_rk", profiles)
self.combobox_add_items("comboBox_end_rk", profiles) self.combobox_add_items("comboBox_end_rk", profiles)
r = self.parent.tableView\
.selectionModel()\
.selectedRows()
if len(r) <= 1:
self.set_combobox_text("comboBox_begin_rk", profiles[0]) self.set_combobox_text("comboBox_begin_rk", profiles[0])
self.set_combobox_text("comboBox_end_rk", profiles[-1]) self.set_combobox_text("comboBox_end_rk", profiles[-1])
else:
self.set_combobox_text("comboBox_begin_rk", profiles[r[0].row()])
self.set_combobox_text("comboBox_end_rk", profiles[r[-1].row()])
@property @property
def profiles(self): def profiles(self):
@@ -167,8 +177,8 @@ class MeshingDialog(PamhyrDialog):
self._begin_cs = self.profiles.index(p1) self._begin_cs = self.profiles.index(p1)
self._end_cs = self.profiles.index(p2) self._end_cs = self.profiles.index(p2)
self._begin_dir = self.get_combobox_text("comboBox_begin_gl") # self._begin_dir = self.get_combobox_text("comboBox_begin_gl")
self._end_dir = self.get_combobox_text("comboBox_end_gl") # self._end_dir = self.get_combobox_text("comboBox_end_gl")
super().accept() super().accept()
+5
View File
@@ -51,6 +51,11 @@ class PlotAC(PamhyrPlot):
self.plot_selected = None self.plot_selected = None
self.next_plot_selected = None self.next_plot_selected = None
@timer
def redraw(self, data):
self._data = data
self.draw()
@timer @timer
def draw(self): def draw(self):
self.init_axes() self.init_axes()
+5
View File
@@ -163,6 +163,11 @@ class PlotRKZ(PamhyrPlot):
) )
self._table.scrollTo(self._table.model().index(ind2, 0)) self._table.scrollTo(self._table.model().index(ind2, 0))
@timer
def redraw(self, data):
self._data = data
self.draw()
@timer @timer
def draw(self): def draw(self):
self.init_axes() self.init_axes()
+5
View File
@@ -162,6 +162,11 @@ class PlotXY(PamhyrPlot):
) )
self._table.scrollTo(self._table.model().index(ind2, 0)) self._table.scrollTo(self._table.model().index(ind2, 0))
@timer
def redraw(self, data):
self._data = data
self.draw()
@timer @timer
def draw(self): def draw(self):
self.init_axes() self.init_axes()
+10 -1
View File
@@ -169,7 +169,7 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def move_down(self, row, parent=QModelIndex()): def move_down(self, row, parent=QModelIndex()):
if row > self._data.number_profiles: if row >= self._data.number_profiles-1:
return return
target = row target = row
@@ -276,3 +276,12 @@ class GeometryReachTableModel(PamhyrTableModel):
) )
) )
self.layoutChanged.emit() self.layoutChanged.emit()
def change_reach(self, new_reach, parent):
self._undo.push(
ChangeReachCommand(
new_reach,
parent
)
)
+32 -5
View File
@@ -22,14 +22,12 @@ from copy import deepcopy
from tools import trace, timer, logger_exception from tools import trace, timer, logger_exception
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack, QMessageBox, QUndoCommand, QUndoStack, QTableView
) )
from Model.Geometry import Reach from Model.Geometry import Reach
from Model.Except import exception_message_box from Model.Except import exception_message_box
from Meshing.Mage import MeshingWithMage
logger = logging.getLogger() logger = logging.getLogger()
@@ -240,7 +238,7 @@ class MeshingCommand(QUndoCommand):
self._mesher = mesher self._mesher = mesher
self._command = command self._command = command
self._profiles = reach.profiles.copy() self._profiles = [p.copy() for p in reach.profiles]
self._profiles.reverse() self._profiles.reverse()
self._new_profiles = None self._new_profiles = None
@@ -264,7 +262,7 @@ class MeshingCommand(QUndoCommand):
**self._data **self._data
) )
self._new_profiles = self._reach.profiles.copy() self._new_profiles = [p.copy() for p in self._reach.profiles]
self._new_profiles.reverse() self._new_profiles.reverse()
else: else:
self._reach.purge() self._reach.purge()
@@ -293,6 +291,35 @@ class PurgeCommand(QUndoCommand):
profile.purge(self._np_purge) profile.purge(self._np_purge)
class ChangeReachCommand(QUndoCommand):
def __init__(self, new_reach, parent):
QUndoCommand.__init__(self)
self._old_reach = parent._study.river.current_reach()
self._new_reach = new_reach
self._parent = parent
def undo(self):
p = self._parent
p._reach = self._old_reach.reach
p._study.river.set_current_reach(self._old_reach)
p.setup_table()
p.update_redraw()
p.find(QTableView, "tableView").selectionModel()\
.selectionChanged\
.connect(p.select_current_profile)
def redo(self):
p = self._parent
p._reach = self._new_reach.reach
p._study.river.set_current_reach(self._new_reach)
p.setup_table()
p.update_redraw()
p.find(QTableView, "tableView").selectionModel()\
.selectionChanged\
.connect(p.select_current_profile)
class ShiftCommand(QUndoCommand): class ShiftCommand(QUndoCommand):
def __init__(self, reach, rows, dx, dy, dz): def __init__(self, reach, rows, dx, dy, dz):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
+50 -19
View File
@@ -32,6 +32,7 @@ from PyQt5.QtGui import (
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QModelIndex, Qt, QSettings, pyqtSlot, QModelIndex, Qt, QSettings, pyqtSlot,
QItemSelectionModel, QCoreApplication, QSize, QItemSelectionModel, QCoreApplication, QSize,
QItemSelection, QItemSelectionRange,
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QMainWindow, QFileDialog, QCheckBox, QApplication, QMainWindow, QFileDialog, QCheckBox,
@@ -47,9 +48,9 @@ from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas from View.Tools.Plot.PamhyrCanvas import MplCanvas
from Meshing.Mage import ( from View.SelectReach.Window import SelectReachWindow
MeshingWithMage, MeshingWithMageMailleurTT
) from Meshing.Internal import InternalMeshing
from View.Geometry.Table import GeometryReachTableModel from View.Geometry.Table import GeometryReachTableModel
from View.Geometry.PlotXY import PlotXY from View.Geometry.PlotXY import PlotXY
@@ -196,6 +197,7 @@ class GeometryWindow(PamhyrWindow):
"action_update_rk": self.update_rk, "action_update_rk": self.update_rk,
"action_purge": self.purge, "action_purge": self.purge,
"action_shift": self.shift, "action_shift": self.shift,
"action_select_reach": self.select_reach,
} }
for action in actions: for action in actions:
@@ -217,9 +219,9 @@ class GeometryWindow(PamhyrWindow):
def _update(self, redraw=False, propagate=True): def _update(self, redraw=False, propagate=True):
if redraw: if redraw:
self._plot_xy.draw() self._plot_xy.redraw(data=self._reach)
self._plot_rkc.draw() self._plot_rkc.redraw(data=self._reach)
self._plot_ac.draw() self._plot_ac.redraw(data=self._reach)
self.select_current_profile() self.select_current_profile()
@@ -285,6 +287,13 @@ class GeometryWindow(PamhyrWindow):
self.tableView.model().blockSignals(False) self.tableView.model().blockSignals(False)
def edit_meshing(self): def edit_meshing(self):
rows = list(
set(
(i.row() for i in self.tableView.selectedIndexes())
)
)
selected_rk = [self._reach.profile(r).rk for r in rows]
try: try:
dlg = MeshingDialog( dlg = MeshingDialog(
reach=self._reach, reach=self._reach,
@@ -295,8 +304,6 @@ class GeometryWindow(PamhyrWindow):
data = { data = {
"step": dlg.space_step, "step": dlg.space_step,
"limites": [dlg.begin_cs, dlg.end_cs], "limites": [dlg.begin_cs, dlg.end_cs],
"directrices": [dlg.begin_dir, dlg.end_dir],
"lplan": dlg.lplan,
"linear": dlg.linear, "linear": dlg.linear,
} }
self._edit_meshing(data) self._edit_meshing(data)
@@ -304,18 +311,31 @@ class GeometryWindow(PamhyrWindow):
logger_exception(e) logger_exception(e)
return return
ind = []
for i in range(self._reach.number_profiles):
if self._reach.profile(i).rk in selected_rk:
ind.append(i)
self.tableView.setFocus()
selection = self.tableView.selectionModel()
index = QItemSelection()
if len(ind) > 0:
for i in ind:
index.append(QItemSelectionRange(
self.tableView.model().index(i, 0))
)
selection.select(
index,
QItemSelectionModel.Rows |
QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Select
)
def _edit_meshing(self, data): def _edit_meshing(self, data):
try: try:
mesher = MeshingWithMageMailleurTT() mesher = InternalMeshing()
self._table.meshing(mesher, data) self._table.meshing(mesher, data)
except Exception as e: except Exception as e:
logger_exception(e) logger_exception(e)
raise ExternFileMissingError(
module="mage",
filename="MailleurTT",
path=MeshingWithMageMailleurTT._path(),
src_except=e
)
def update_rk(self): def update_rk(self):
try: try:
@@ -338,14 +358,11 @@ class GeometryWindow(PamhyrWindow):
def _update_rk(self, data): def _update_rk(self, data):
try: try:
mesher = MeshingWithMageMailleurTT() mesher = InternalMeshing()
self._table.update_rk(mesher, data) self._table.update_rk(mesher, data)
except Exception as e: except Exception as e:
logger_exception(e) logger_exception(e)
raise ExternFileMissingError( raise ExternFileMissingError(
module="mage",
filename="MailleurTT",
path=MeshingWithMageMailleurTT._path(),
src_except=e src_except=e
) )
@@ -535,6 +552,20 @@ class GeometryWindow(PamhyrWindow):
logger_exception(e) logger_exception(e)
return return
def select_reach(self):
try:
dlg = SelectReachWindow(
study=self._study,
trad=self._trad,
parent=self
)
if dlg.exec():
self._table.change_reach(dlg.reach, self)
except Exception as e:
logger_exception(e)
return
def duplicate(self): def duplicate(self):
rows = [ rows = [
row.row() for row in row.row() for row in
+46 -1
View File
@@ -244,7 +244,7 @@ class InitialConditionTableModel(PamhyrTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit() self.layoutChanged.emit()
def import_from_results(self, index, results): def import_from_results(self, results):
if results is None: if results is None:
logger.error("No results data") logger.error("No results data")
return return
@@ -281,6 +281,51 @@ class InitialConditionTableModel(PamhyrTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit() self.layoutChanged.emit()
def read_from_ini(self, file_name):
reach_id = self._data.river.enable_edges().index(self._reach) + 1
logger.debug(f"Import initial conditions from {file_name}")
data = []
with open(file_name, encoding="utf-8") as ini_file:
for line in ini_file:
if not (line.startswith("#") or
line.startswith("*") or
line.startswith("$")):
line_split = line.split()
formated = False
if len(line_split[0]) > 3:
formated = True
elif len(line_split[2]) > 10:
formated = True
elif len(line_split[3]) > 11:
formated = True
elif len(line_split[4]) > 9:
formated = True
if formated: # old PamHyr format
if int(line[1:4]) == reach_id:
data.append([line[31:40],
line[10:20],
line[20:31]])
else:
if int(line_split[0]) == reach_id:
data.append([line_split[4],
line_split[2],
line_split[3]])
self._undo.push(
ReplaceDataCommand(
self._lst,
list(
map(
lambda d: self._lst.new_from_data(*d),
data
)
)
)
)
def undo(self): def undo(self):
self._undo.undo() self._undo.undo()
self.layoutChanged.emit() self.layoutChanged.emit()
+35 -11
View File
@@ -23,6 +23,7 @@ from tools import trace, timer, logger_exception
from View.Tools.PamhyrWindow import PamhyrWindow from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5 import QtWidgets
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
) )
@@ -30,7 +31,7 @@ from PyQt5.QtGui import (
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot, QCoreApplication, QModelIndex, pyqtSlot,
QRect, QRect, QSettings, QItemSelectionModel,
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
@@ -219,6 +220,8 @@ class InitialConditionsWindow(PamhyrWindow):
def _update_plot(self): def _update_plot(self):
self.plot_1.draw() self.plot_1.draw()
self.plot_2.draw() self.plot_2.draw()
self.plot_1.idle()
self.plot_2.idle()
def _propagated_update(self, key=Modules(0)): def _propagated_update(self, key=Modules(0)):
if Modules.GEOMETRY not in key: if Modules.GEOMETRY not in key:
@@ -260,17 +263,33 @@ class InitialConditionsWindow(PamhyrWindow):
self._update() self._update()
def import_from_file(self): def import_from_file(self):
workdir = os.path.dirname(self._study.filename) options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
return self.file_dialog( file_types = [
select_file="ExistingFile", self._trad["file_bin"],
callback=lambda d: self._import_from_file(d[0]), self._trad["file_ini"],
directory=workdir, self._trad["file_all"],
default_suffix=".BIN", ]
file_filter=["Mage (*.BIN)"], file_name, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
) )
def _import_from_file(self, file_name): if file_name != "":
size = os.stat(file_name).st_size
# self._table.import_geometry(0, file_name)
if file_name[-4:] == ".BIN":
self._import_from_bin_file(file_name)
elif file_name[-4:].upper() == ".INI":
self._import_from_ini_file(file_name)
def _import_from_bin_file(self, file_name):
solver = Mage8("dummy") solver = Mage8("dummy")
name = os.path.basename(file_name)\ name = os.path.basename(file_name)\
.replace(".BIN", "") .replace(".BIN", "")
@@ -292,13 +311,18 @@ class InitialConditionsWindow(PamhyrWindow):
) )
dlg.exec_() dlg.exec_()
results = self._tmp_results results = self._tmp_results
if results.is_valid:
self._import_from_results(results) self._import_from_results(results)
def _import_from_results(self, results): def _import_from_results(self, results):
logger.debug(f"import from results: {results}") logger.debug(f"import from results: {results}")
row = self.index_selected_row() self._table.import_from_results(results)
self._update()
self._table.import_from_results(row, results) def _import_from_ini_file(self, file_name):
logger.debug(f"import from INI file: {file_name}")
self._table.read_from_ini(file_name)
self._update()
def move_up(self): def move_up(self):
row = self.index_selected_row() row = self.index_selected_row()
+9
View File
@@ -34,6 +34,15 @@ class ICTranslate(MainTranslate):
self._dict["discharge"] = self._dict["unit_discharge"] self._dict["discharge"] = self._dict["unit_discharge"]
self._dict["rk"] = self._dict["unit_rk"] self._dict["rk"] = self._dict["unit_rk"]
self._dict["open_file"] = _translate(
"InitialCondition", "Open a file")
self._dict["file_bin"] = _translate(
"InitialCondition", "Mage results file (*.BIN)")
self._dict["file_ini"] = _translate(
"InitialCondition", "Mage initial conditions file (*.INI *.ini)")
self._dict["file_all"] = _translate(
"InitialCondition", "All files (*)")
self._sub_dict["table_headers"] = { self._sub_dict["table_headers"] = {
# "name": _translate("InitialCondition", "Name"), # "name": _translate("InitialCondition", "Name"),
"rk": self._dict["unit_rk"], "rk": self._dict["unit_rk"],
+9 -10
View File
@@ -129,49 +129,49 @@ class InitialConditionTableModel(PamhyrTableModel):
row = index.row() row = index.row()
column = index.column() column = index.column()
if self._headers[column] is "name": if self._headers[column] == "name":
n = self._lst[row].name n = self._lst[row].name
if n is None or n == "": if n is None or n == "":
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "reach": elif self._headers[column] == "reach":
n = self._lst[row].reach n = self._lst[row].reach
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return next(filter( return next(filter(
lambda edge: edge.id == n, self._river.edges() lambda edge: edge.id == n, self._river.edges()
)).name )).name
elif self._headers[column] is "start_rk": elif self._headers[column] == "start_rk":
n = self._lst[row].start_rk n = self._lst[row].start_rk
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "end_rk": elif self._headers[column] == "end_rk":
n = self._lst[row].end_rk n = self._lst[row].end_rk
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "concentration": elif self._headers[column] == "concentration":
n = self._lst[row].concentration n = self._lst[row].concentration
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "eg": elif self._headers[column] == "eg":
n = self._lst[row].eg n = self._lst[row].eg
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "em": elif self._headers[column] == "em":
n = self._lst[row].em n = self._lst[row].em
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "ed": elif self._headers[column] == "ed":
n = self._lst[row].ed n = self._lst[row].ed
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "rate": elif self._headers[column] == "rate":
n = self._lst[row].rate n = self._lst[row].rate
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
@@ -194,7 +194,6 @@ class InitialConditionTableModel(PamhyrTableModel):
) )
) )
elif self._headers[column] == "reach": elif self._headers[column] == "reach":
print(self._river.edge(value).id)
self._undo.push( self._undo.push(
SetCommandSpec( SetCommandSpec(
self._lst, row, self._headers[column], self._lst, row, self._headers[column],
@@ -55,24 +55,24 @@ class InitialConditionTableDefaultModel(PamhyrTableModel):
row = index.row() row = index.row()
column = index.column() column = index.column()
if self._headers[column] is "name": if self._headers[column] == "name":
return self._data[row].name return self._data[row].name
elif self._headers[column] is "concentration": elif self._headers[column] == "concentration":
n = self._data[row].concentration n = self._data[row].concentration
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "eg": elif self._headers[column] == "eg":
n = self._data[row].eg n = self._data[row].eg
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "em": elif self._headers[column] == "em":
n = self._data[row].em n = self._data[row].em
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
return n return n
elif self._headers[column] is "ed": elif self._headers[column] == "ed":
n = self._data[row].ed n = self._data[row].ed
if n is None: if n is None:
return self._trad['not_associated'] return self._trad['not_associated']
@@ -281,9 +281,7 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
self._table_spec.add(rows[0]) self._table_spec.add(rows[0])
def delete(self): def delete(self):
print("del")
rows = self.index_selected_rows() rows = self.index_selected_rows()
if len(rows) == 0: if len(rows) == 0:
print("len 0")
return return
self._table_spec.delete(rows) self._table_spec.delete(rows)
+47 -3
View File
@@ -19,7 +19,7 @@
import logging import logging
import traceback import traceback
from tools import trace, timer from tools import trace, timer, old_pamhyr_date_to_timestamp
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, Qt, QVariant, QAbstractTableModel,
@@ -38,7 +38,7 @@ from View.LateralContribution.UndoCommand import (
SetNameCommand, SetEdgeCommand, SetTypeCommand, SetNameCommand, SetEdgeCommand, SetTypeCommand,
SetBeginCommand, SetEndCommand, SetBeginCommand, SetEndCommand,
AddCommand, DelCommand, SortCommand, AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand, MoveCommand, PasteCommand, DuplicateCommand, ImportCommand,
) )
from Model.LateralContribution.LateralContributionTypes import ( from Model.LateralContribution.LateralContributionTypes import (
@@ -116,7 +116,10 @@ class ComboBoxDelegate(QItemDelegate):
def setModelData(self, editor, model, index): def setModelData(self, editor, model, index):
text = str(editor.currentText()) text = str(editor.currentText())
if self._mode == "rk": if self._data is None:
value = text
else:
if self._mode == "rk" and self._data.reach is not None:
profiles = list( profiles = list(
filter( filter(
lambda p: p.display_name() == text, lambda p: p.display_name() == text,
@@ -293,3 +296,44 @@ class TableModel(PamhyrTableModel):
self.endMoveRows() self.endMoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def read_from_lat(self, file_name):
logger.debug(f"Import lateral contributions from {file_name}")
data = []
current_reach = -1
with open(file_name, encoding="utf-8") as lat_file:
for line in lat_file:
if not (line.startswith("#") or
line.startswith("*") or
len(line) < 1):
line = line.split()
if line[0] == "$":
current_reach = int(line[1]) - 1
if (current_reach <= len(self._data.enable_edges()) and
current_reach >= 0):
data.append(
[self._data.enable_edges()[current_reach],
float(line[2]),
float(line[3])
]
)
else:
if (current_reach <= len(self._data.enable_edges()) and
current_reach >= 0):
data[-1].append(
[
old_pamhyr_date_to_timestamp(line[0]),
float(line[1])
]
)
self.layoutAboutToBeChanged.emit()
self._undo.push(
ImportCommand(
self._lst, self._tab, data
)
)
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
+57 -16
View File
@@ -28,6 +28,10 @@ from Model.LateralContribution.LateralContributionList import (
LateralContributionList LateralContributionList
) )
from Model.LateralContribution.LateralContributionTypes import (
NotDefined, LateralContrib,
)
class SetNameCommand(QUndoCommand): class SetNameCommand(QUndoCommand):
def __init__(self, lcs, tab, index, new_value): def __init__(self, lcs, tab, index, new_value):
@@ -143,13 +147,13 @@ class DelCommand(QUndoCommand):
self._tab = tab self._tab = tab
self._rows = rows self._rows = rows
self._bc = [] self._lc = []
for row in rows: for row in rows:
self._bc.append((row, self._lcs.get(self._tab, row))) self._lc.append((row, self._lcs.get(self._tab, row)))
self._bc.sort() self._lc.sort()
def undo(self): def undo(self):
for row, el in self._bc: for row, el in self._lc:
self._lcs.insert(self._tab, row, el) self._lcs.insert(self._tab, row, el)
def redo(self): def redo(self):
@@ -213,36 +217,73 @@ class MoveCommand(QUndoCommand):
class PasteCommand(QUndoCommand): class PasteCommand(QUndoCommand):
def __init__(self, lcs, tab, row, bc): def __init__(self, lcs, tab, row, lc):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._lcs = lcs self._lcs = lcs
self._tab = tab self._tab = tab
self._row = row self._row = row
self._bc = deepcopy(bc) self._lc = deepcopy(lc)
self._bc.reverse() self._lc.reverse()
def undo(self): def undo(self):
self._lcs.delete(self._tab, self._bc) self._lcs.delete(self._tab, self._lc)
def redo(self): def redo(self):
for bc in self._bc: for lc in self._lc:
self._lcs.insert(self._tab, self._row, bc) self._lcs.insert(self._tab, self._row, lc)
class ImportCommand(QUndoCommand):
def __init__(self, lcs, tab, data):
QUndoCommand.__init__(self)
self._tab = tab
self._lcs = lcs
self._data = data
self._old_rows = list(range(len(self._lcs.get_tab(self._tab))))
self._new_rows = list(range(len(self._data)))
self._new_lc = None
self._old_lc = []
for row in self._old_rows:
self._old_lc.append((row, self._lcs.get(self._tab, row)))
def undo(self):
self._lcs.delete_i(self._tab, self._new_rows)
for row, el in self._old_lc:
self._lcs.insert(self._tab, row, el)
def redo(self):
self._lcs.delete_i(self._tab, self._old_rows)
if self._new_lc is None:
self._new_lc = []
for row, data in enumerate(self._data):
new = LateralContrib(status=self._lcs._status)
new.edge = data[0]
new.begin_rk = data[1]
new.end_rk = data[2]
for i, val in enumerate(data[3:]):
new.insert(i, val)
self._new_lc.append(new)
for row, el in enumerate(self._new_lc):
self._lcs.insert(self._tab, row, el)
class DuplicateCommand(QUndoCommand): class DuplicateCommand(QUndoCommand):
def __init__(self, lcs, tab, rows, bc): def __init__(self, lcs, tab, rows, lc):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._lcs = lcs self._lcs = lcs
self._tab = tab self._tab = tab
self._rows = rows self._rows = rows
self._bc = deepcopy(bc) self._lc = deepcopy(lc)
self._bc.reverse() self._lc.reverse()
def undo(self): def undo(self):
self._lcs.delete(self._tab, self._bc) self._lcs.delete(self._tab, self._lc)
def redo(self): def redo(self):
for bc in self._lcs: for lc in self._lcs:
self._lcs.insert(self._tab, self._rows[0], bc) self._lcs.insert(self._tab, self._rows[0], lc)
+27 -1
View File
@@ -22,6 +22,7 @@ from tools import trace, timer
from View.Tools.PamhyrWindow import PamhyrWindow from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5 import QtWidgets
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
) )
@@ -29,7 +30,7 @@ from PyQt5.QtGui import (
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot, QCoreApplication, QModelIndex, pyqtSlot,
QRect, QRect, QSettings,
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
@@ -153,6 +154,8 @@ class LateralContributionWindow(PamhyrWindow):
) )
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_import").triggered.connect(
self.import_from_file)
self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit) self.find(QAction, "action_edit").triggered.connect(self.edit)
@@ -297,3 +300,26 @@ class LateralContributionWindow(PamhyrWindow):
parent=self parent=self
) )
win.show() win.show()
def import_from_file(self):
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
file_types = [
self._trad["file_lat"],
self._trad["file_all"],
]
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
)
if filename != "":
tab = self.current_tab()
self._table[tab].read_from_lat(filename)
@@ -52,6 +52,11 @@ class LCTranslate(MainTranslate):
self._dict["x"] = _translate("Geometry", "X (m)") self._dict["x"] = _translate("Geometry", "X (m)")
self._dict["y"] = _translate("Geometry", "Y (m)") self._dict["y"] = _translate("Geometry", "Y (m)")
self._dict["z"] = _translate("Geometry", "Z (m)") self._dict["z"] = _translate("Geometry", "Z (m)")
self._dict["file_lat"] = _translate(
"LateralContribution",
"Mage lateral contributions file (*.LAT *.lat)")
self._dict["file_all"] = _translate(
"LateralContribution", "All files (*)")
self._sub_dict["table_headers"] = { self._sub_dict["table_headers"] = {
"name": self._dict["name"], "name": self._dict["name"],
+172 -23
View File
@@ -30,6 +30,7 @@ from platformdirs import user_cache_dir
from Solver.AdisTS import AdisTS from Solver.AdisTS import AdisTS
from Solver.Mage import Mage8 from Solver.Mage import Mage8
from Solver.RubarBE import Rubar3
from tools import logger_exception from tools import logger_exception
from PyQt5 import QtGui from PyQt5 import QtGui
@@ -77,14 +78,18 @@ from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
from View.AdditionalFiles.Window import AddFileListWindow from View.AdditionalFiles.Window import AddFileListWindow
from View.REPLines.Window import REPLineListWindow from View.REPLines.Window import REPLineListWindow
from View.SolverParameters.Window import SolverParametersWindow from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow from View.RunSolver.Window import (
SelectSolverWindow,
SolverLogWindow,
CompareSolversWindow
)
from View.Results.Window import ResultsWindow
from View.RunSolver.WindowAdisTS import ( from View.RunSolver.WindowAdisTS import (
SelectSolverWindowAdisTS, SelectSolverWindowAdisTS,
SolverLogWindowAdisTS SolverLogWindowAdisTS
) )
from View.CheckList.Window import CheckListWindow from View.CheckList.Window import CheckListWindow
from View.CheckList.WindowAdisTS import CheckListWindowAdisTS from View.CheckList.WindowAdisTS import CheckListWindowAdisTS
from View.Results.Window import ResultsWindow
from View.Results.WindowAdisTS import ResultsWindowAdisTS from View.Results.WindowAdisTS import ResultsWindowAdisTS
from View.Results.ReadingResultsDialog import ReadingResultsDialog from View.Results.ReadingResultsDialog import ReadingResultsDialog
from View.Debug.Window import ReplWindow from View.Debug.Window import ReplWindow
@@ -93,6 +98,10 @@ from View.Pollutants.Window import PollutantsWindow
from View.D90AdisTS.Window import D90AdisTSWindow from View.D90AdisTS.Window import D90AdisTSWindow
from View.DIFAdisTS.Window import DIFAdisTSWindow from View.DIFAdisTS.Window import DIFAdisTSWindow
from Solver.Solvers import GenericSolver
from Model.Results.Results import Results
# Optional internal display of documentation for make the application # Optional internal display of documentation for make the application
# package lighter... # package lighter...
try: try:
@@ -135,7 +144,7 @@ define_model_action = [
"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_menu_additional_file", "action_menu_edit_hydraulic_structures", "action_menu_additional_file",
"action_menu_results_last", "action_open_results_from_file", "action_menu_results_last", "action_open_results_from_file",
"action_menu_boundary_conditions_sediment", "action_compare_results", "action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_rk", "action_menu_rep_additional_lines", "action_menu_output_rk",
"action_menu_run_adists", "action_menu_pollutants", "action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90", "action_menu_dif", "action_menu_d90", "action_menu_dif",
@@ -186,10 +195,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
title = "(dbg) " if self.conf.debug else "" title = "(dbg) " if self.conf.debug else ""
if self._study is not None: if self._study is not None:
title += f"Pamhyr2 - {self._study.name}" title += f"Pamhyr2 (Le Coz) - {self._study.name}"
self.setWindowTitle(title) self.setWindowTitle(title)
else: else:
title += "Pamhyr2" title += "Pamhyr2 (Le Coz)"
self.setWindowTitle(title) self.setWindowTitle(title)
def setup_tab(self): def setup_tab(self):
@@ -280,6 +289,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_close": self.close_model, "action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results, "action_menu_results_last": self.open_last_results,
"action_open_results_from_file": self.open_results_from_file, "action_open_results_from_file": self.open_results_from_file,
"action_compare_results": self.compare_results,
"action_open_results_adists": self.open_results_adists, "action_open_results_adists": self.open_results_adists,
# Help # Help
"action_menu_pamhyr_users_wiki": self.open_doc_user, "action_menu_pamhyr_users_wiki": self.open_doc_user,
@@ -339,10 +349,12 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
def closeEvent(self, event): def closeEvent(self, event):
if not self._close_question: if not self._close_question:
if self._study is not None and not self._study.is_saved: if self._study is not None and not self._study.is_saved:
if self.dialog_close(cancel=False): if self.dialog_close():
# PAMHYR is close correctly (no crash) # PAMHYR is close correctly (no crash)
self.conf.set_close_correctly() self.conf.set_close_correctly()
super(ApplicationWindow, self).closeEvent(event) super(ApplicationWindow, self).closeEvent(event)
else:
event.ignore()
else: else:
super(ApplicationWindow, self).closeEvent(event) super(ApplicationWindow, self).closeEvent(event)
@@ -522,7 +534,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self.update() self.update()
def close_model(self): def close_model(self):
if not self.dialog_close(cancel=True): if not self.dialog_close():
return return
self._study = None self._study = None
@@ -824,20 +836,18 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return file return file
def dialog_close(self, cancel=True): def dialog_close(self):
dlg = QMessageBox(self) dlg = QMessageBox(self)
dlg.setWindowTitle(self._trad["mb_close_title"]) dlg.setWindowTitle(self._trad["mb_close_title"])
dlg.setText(self._trad["mb_close_msg"]) dlg.setText(self._trad["mb_close_msg"])
opt = QMessageBox.Save | QMessageBox.Close opt = QMessageBox.Save | QMessageBox.Close
if cancel:
opt |= QMessageBox.Cancel opt |= QMessageBox.Cancel
dlg.setStandardButtons(opt) dlg.setStandardButtons(opt)
dlg.setIcon(QMessageBox.Warning) dlg.setIcon(QMessageBox.Warning)
dlg.button(QMessageBox.Save).setText(self._trad["Save"]) dlg.button(QMessageBox.Save).setText(self._trad["Save"])
dlg.button(QMessageBox.Close).setText(self._trad["Close"]) dlg.button(QMessageBox.Close).setText(self._trad["Close"])
if cancel:
dlg.button(QMessageBox.Cancel).setText(self._trad["Cancel"]) dlg.button(QMessageBox.Cancel).setText(self._trad["Cancel"])
res = dlg.exec() res = dlg.exec()
@@ -1310,7 +1320,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
# self.conf.solvers)) # self.conf.solvers))
# solver = next(filter(lambda x: x.name == "AdisTS-WC", # solver = next(filter(lambda x: x.name == "AdisTS-WC",
# self.conf.solvers)) # self.conf.solvers))
# print(solver._type)
# self.run_solver(solver) # self.run_solver(solver)
run = SelectSolverWindowAdisTS( run = SelectSolverWindowAdisTS(
@@ -1406,7 +1415,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if type(results) is str: if type(results) is str:
logger.info(f"Open results from {os.path.dirname(results)}") logger.info(f"Open results from {os.path.dirname(results)}")
if ".BIN" in results:
name = os.path.basename(results).replace(".BIN", "") name = os.path.basename(results).replace(".BIN", "")
elif "profil." in results:
name = os.path.basename(results).replace("profil.", "")
def reading_fn(): def reading_fn():
self._tmp_results = solver.results( self._tmp_results = solver.results(
@@ -1423,22 +1435,25 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if results is None: if results is None:
return return
# results does not have values, for example if geometry missmatch
if not results.is_valid:
return
# Windows already opened # Windows already opened
if self.sub_window_exists( if self.sub_window_exists(
ResultsWindow, ResultsWindow,
data=[ data=[
self._study, self._study,
None, # No config None, # No config,
solver, results._solver,
results results._repertory,
] results._name]
): ):
return return
res = ResultsWindow( res = ResultsWindow(
study=self._study, study=self._study,
solver=solver, results=[results],
results=results,
parent=self parent=self
) )
res.show() res.show()
@@ -1487,7 +1502,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self._study, self._study,
None, # No config None, # No config
solver, solver,
results results._repertory,
results._name,
] ]
): ):
return return
@@ -1525,9 +1541,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if self._study is None: if self._study is None:
return return
if self._study.filename == "":
return
options = QFileDialog.Options() options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog options |= QFileDialog.DontUseNativeDialog
@@ -1536,8 +1549,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
dialog.setFileMode(QFileDialog.FileMode.ExistingFile) dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
dialog.setDefaultSuffix(".BIN") dialog.setDefaultSuffix(".BIN")
# dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden) # dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
dialog.setNameFilters(['Mage (*.BIN)']) dialog.setNameFilters(['Mage (*.BIN)', 'Rubar3 (profil.*)'])
if self._study.filename is not None:
if self._last_solver is None: if self._last_solver is None:
dialog.setDirectory( dialog.setDirectory(
os.path.dirname(self._study.filename) os.path.dirname(self._study.filename)
@@ -1550,11 +1564,146 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if dialog.exec_(): if dialog.exec_():
file_name = dialog.selectedFiles() file_name = dialog.selectedFiles()
logger.info(f"Select results: {file_name}") logger.info(f"Select results: {file_name}")
solver = None
if ".BIN" in file_name:
solver = Mage8("Mage8")
else:
solver = Rubar3("Rubar3")
self.open_solver_results( self.open_solver_results(
Mage8("Mage"), solver,
results=file_name[0] results=file_name[0]
) )
def compare_results(self):
if self._study is None:
return
run = CompareSolversWindow(
study=self._study,
config=self.conf,
parent=self
)
if run.exec():
results = self.diff_results(run.solver1,
run.solver2)
else:
return
# At least one result not available
if results is None:
return
# Windows already opened
if self.sub_window_exists(
ResultsWindow,
data=[self._study, None] +
[r._solver for r in results] +
[r._repertory for r in results] +
[r._name for r in results]
):
return
res = ResultsWindow(
study=self._study,
results=results,
parent=self
)
res.show()
def diff_results(self, solver1, solver2):
if solver1 is None:
# TODO message
return None
if solver2 is None:
# TODO message
return None
solver3 = GenericSolver(solver1.name+" - "+solver2.name)
solver4 = GenericSolver(solver1.name)
solver5 = GenericSolver(solver2.name)
result1 = solver1.results(
self._study,
self._solver_workdir(solver1),
)
if result1 is None:
# TODO message
return None
result2 = solver2.results(
self._study,
self._solver_workdir(solver2),
)
if result2 is None:
# TODO message
return None
if result2.get("nb_reach") != result1.get("nb_reach"):
# TODO message
return None
if result2.get("nb_profile") != result1.get("nb_profile"):
# TODO message
return None
# return [result1, result2]
result3 = Results(self._study, solver3)
result4 = Results(self._study, solver1)
result5 = Results(self._study, solver2)
result3.set("nb_reach", result1.get("nb_reach"))
result4.set("nb_reach", result1.get("nb_reach"))
result5.set("nb_reach", result1.get("nb_reach"))
result3.set("nb_profile", result1.get("nb_profile"))
result4.set("nb_profile", result1.get("nb_profile"))
result5.set("nb_profile", result1.get("nb_profile"))
ts = sorted(list(result1.get("timestamps").intersection(
result2.get("timestamps"))))
result3.set("timestamps", ts)
result4.set("timestamps", ts)
result5.set("timestamps", ts)
for i in range(int(result1.get("nb_reach"))):
# Add reach to results reach list
r = result3.river.add(i)
r = result4.river.add(i)
r = result5.river.add(i)
for timestamp in result3.get("timestamps"):
for r in range(int(result1.get("nb_reach"))):
reach1 = result1.river.reach(r)
reach2 = result2.river.reach(r)
reach3 = result3.river.reach(r)
reach4 = result4.river.reach(r)
reach5 = result5.river.reach(r)
for p, (profile1, profile2) in enumerate(zip(
reach1.profiles, reach2.profiles)):
for key in ["Z", "Q", "V"]:
d1 = profile1.get_ts_key(timestamp, key)
d2 = profile2.get_ts_key(timestamp, key)
d = d1-d2
reach3.set(p, timestamp, key, d)
reach4.set(p, timestamp, key, d1)
reach5.set(p, timestamp, key, d2)
limits = reach3.profile(p).geometry.get_water_limits(
reach3.profile(p).get_ts_key(timestamp, "Z")
)
reach3.set(
p, timestamp,
"water_limits",
limits
)
limits = profile1.get_ts_key(timestamp, "water_limits")
reach4.set(p, timestamp, "water_limits", limits)
limits = profile2.get_ts_key(timestamp, "water_limits")
reach5.set(p, timestamp, "water_limits", limits)
return [result4, result5, result3]
def open_results_adists(self): def open_results_adists(self):
if self._study is None: if self._study is None:
return return
+2 -1
View File
@@ -475,7 +475,7 @@ class GraphWidget(QGraphicsView):
def setup_scene(self, min_size, max_size, size): def setup_scene(self, min_size, max_size, size):
scene = QGraphicsScene(self) scene = QGraphicsScene(self)
scene.setItemIndexMethod(QGraphicsScene.NoIndex) scene.setItemIndexMethod(QGraphicsScene.NoIndex)
scene.setSceneRect(0, 0, 2000, 2000) scene.setSceneRect(0, 0, 6000, 6000)
self.setScene(scene) self.setScene(scene)
self.setCacheMode(QGraphicsView.CacheBackground) self.setCacheMode(QGraphicsView.CacheBackground)
@@ -486,6 +486,7 @@ class GraphWidget(QGraphicsView):
self.scale(1, 1) self.scale(1, 1)
self.previousScale = 1 self.previousScale = 1
self.centerOn(1000.0, 1000.0)
if min_size: if min_size:
self.setMinimumSize(*min_size) self.setMinimumSize(*min_size)
+1 -1
View File
@@ -35,5 +35,5 @@ class EditReservoirTranslate(ReservoirTranslate):
self._sub_dict["table_headers"] = { self._sub_dict["table_headers"] = {
"z": self._dict["unit_elevation"], "z": self._dict["unit_elevation"],
"area": self._dict["unit_area"], "area": self._dict["unit_area_he"],
} }
+78
View File
@@ -0,0 +1,78 @@
# ShiftDialog.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtWidgets import QDoubleSpinBox, QPushButton
class CoordinatesDialog(PamhyrDialog):
_pamhyr_ui = "GeotiffCoordinates"
_pamhyr_name = "ImageCoordinates"
def __init__(self, x, y, trad=None, parent=None):
super(CoordinatesDialog, self).__init__(
title=trad[self._pamhyr_name],
trad=trad,
options=[],
parent=parent
)
self.spinbox = {}
self._init_default_values(x, y)
self.setup_connections()
def _init_default_values(self, x, y):
self._x = x
self._y = y
self._values = {"bottom": x[0],
"top": x[1],
"left": y[0],
"right": y[1]}
self.spinbox = {}
for t in self._values:
self.spinbox[t] = self.find(QDoubleSpinBox, f"doubleSpinBox_{t}")
self.reset(t)
def setup_connections(self):
for t in self._values:
self.find(QPushButton,
f"pushButton_{t}").clicked.connect(
lambda state, x=t: self.reset(x))
def reset(self, t):
self.spinbox[t].setValue(self._values[t])
@property
def x(self):
return [self._values["bottom"], self._values["top"]]
@property
def y(self):
return [self._values["left"], self._values["right"]]
@property
def values(self):
return [self._values[t] for t in self._values]
def accept(self):
for t in self._values:
self._values[t] = self.get_double_spin_box(f"doubleSpinBox_{t}")
super().accept()
def reject(self):
self.close()
@@ -0,0 +1,168 @@
# CustomExport.py -- Pamhyr
# Copyright (C) 2023-2025 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 View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtWidgets import (
QRadioButton, QCheckBox, QVBoxLayout, QLabel, QFrame
)
from View.Results.translate import ResultsTranslate
class CustomExportDialog(PamhyrDialog):
_pamhyr_ui = "CustomExportAdisDialog"
_pamhyr_name = "Custom Plot Selection"
def __init__(self, parent=None):
trad = ResultsTranslate()
super(CustomExportDialog, self).__init__(
title=trad[self._pamhyr_name],
options=[],
trad=trad,
parent=parent
)
self._available_values_x = self._trad.get_dict("values_x")
self._available_values_y = self._trad.get_dict("values_y")
self.setup_radio_buttons_x()
self.setup_radio_buttons_res()
self.setup_label()
self.setup_envelop_box()
self.setup_check_boxes()
self.value = None
def setup_radio_buttons_x(self):
self._radio = []
layout = self.find(QVBoxLayout, "verticalLayout_x")
for value in self._available_values_x:
btn = QRadioButton(
self._available_values_x[value],
parent=self
)
self._radio.append((value, btn))
layout.addWidget(btn)
self._radio[0][1].setChecked(True)
layout.addStretch()
def setup_radio_buttons_res(self):
self._radio2 = []
layout = self.find(QVBoxLayout, "verticalLayout_pol")
label = self.find(QLabel, "label_3")
label.setText(self._trad["solver"])
for solver in self.parent._solvers:
btn = QRadioButton(
solver.name,
parent=self
)
self._radio2.append((solver.name, btn))
layout.addWidget(btn)
self._radio2[0][1].setChecked(True)
if len(self.parent._results) == 1:
self._radio2[0][1].setEnabled(False)
label.setEnabled(False)
else:
self._radio2[0][1].setEnabled(True)
label.setEnabled(True)
layout.addStretch()
def setup_label(self):
self._label = self.find(QLabel, "label_4")
self._label.setFrameStyle(QFrame.StyledPanel)
self._label.setStyleSheet('background-color: white')
self.set_label()
for r in self._radio:
r[1].clicked.connect(self.set_label)
def set_label(self):
if self._radio[0][1].isChecked():
self._label.setText(self._parent.text_bief() + "\n" +
self._parent.text_time())
else:
self._label.setText(self._parent.text_bief() + "\n" +
self._parent.text_profile())
def setup_envelop_box(self):
layout = self.find(QVBoxLayout, "verticalLayout_x")
self._envelop = QCheckBox(
self._trad["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():
if len(self.parent._results) > 1:
self._envelop.setEnabled(False)
else:
self._envelop.setEnabled(True)
else:
self._envelop.setEnabled(False)
def setup_check_boxes(self):
self._check = []
layout = self.find(QVBoxLayout, "verticalLayout_y")
for value in self._available_values_y:
btn = QCheckBox(
self._available_values_y[value],
parent=self
)
self._check.append((value, btn))
layout.addWidget(btn)
self._check[0][1].setChecked(True)
layout.addStretch()
def accept(self):
x = next(
filter(
lambda r: r[1].isChecked(),
self._radio
)
)[0]
y = list(
map(
lambda b: b[0],
filter(
lambda b: b[1].isChecked(),
self._check
)
)
)
res = 0
for i, b in enumerate(self._radio2):
if b[1].isChecked():
res = i
break
self.value = x, y, self._envelop.isChecked(), res
super().accept()
@@ -1,5 +1,5 @@
# CustomPlotValuesSelectionDialog.py -- Pamhyr # CustomExportAdis.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE # Copyright (C) 2023-2025 INRAE
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
from View.Tools.PamhyrWindow import PamhyrDialog from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QRadioButton, QCheckBox, QVBoxLayout, QRadioButton, QCheckBox, QVBoxLayout, QLabel, QFrame
) )
from View.Results.translate import ResultsTranslate from View.Results.translate import ResultsTranslate
@@ -50,6 +50,7 @@ class CustomExportAdisDialog(PamhyrDialog):
self.setup_radio_buttons_x() self.setup_radio_buttons_x()
self.setup_radio_buttons_pol() self.setup_radio_buttons_pol()
self.setup_label()
self.setup_check_boxes() self.setup_check_boxes()
self.value = None self.value = None
@@ -84,6 +85,22 @@ class CustomExportAdisDialog(PamhyrDialog):
self._radio2[0][1].setChecked(True) self._radio2[0][1].setChecked(True)
layout.addStretch() layout.addStretch()
def setup_label(self):
self._label = self.find(QLabel, "label_4")
self._label.setFrameStyle(QFrame.StyledPanel)
self._label.setStyleSheet('background-color: white')
self.set_label()
for r in self._radio:
r[1].clicked.connect(self.set_label)
def set_label(self):
if self._radio[0][1].isChecked():
self._label.setText(self._parent.text_bief() + "\n" +
self._parent.text_time())
else:
self._label.setText(self._parent.text_bief() + "\n" +
self._parent.text_profile())
def setup_check_boxes(self): def setup_check_boxes(self):
self._check = [] self._check = []
layout = self.find(QVBoxLayout, "verticalLayout_y") layout = self.find(QVBoxLayout, "verticalLayout_y")
@@ -19,7 +19,7 @@
from View.Tools.PamhyrWindow import PamhyrDialog from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QRadioButton, QCheckBox, QVBoxLayout, QRadioButton, QCheckBox, QVBoxLayout, QLabel, QFrame
) )
from View.Results.CustomPlot.Translate import CustomPlotTranslate from View.Results.CustomPlot.Translate import CustomPlotTranslate
@@ -38,10 +38,12 @@ class CustomPlotValuesSelectionDialog(PamhyrDialog):
parent=parent parent=parent
) )
self._parent = parent
self._available_values_x = self._trad.get_dict("values_x") self._available_values_x = self._trad.get_dict("values_x")
self._available_values_y = self._trad.get_dict("values_y") self._available_values_y = self._trad.get_dict("values_y")
self.setup_radio_buttons() self.setup_radio_buttons()
self.setup_label()
self.setup_envelop_box() self.setup_envelop_box()
self.setup_check_boxs() self.setup_check_boxs()
@@ -62,6 +64,22 @@ class CustomPlotValuesSelectionDialog(PamhyrDialog):
self._radio[0][1].setChecked(True) self._radio[0][1].setChecked(True)
layout.addStretch() layout.addStretch()
def setup_label(self):
self._label = self.find(QLabel, "label_3")
self._label.setFrameStyle(QFrame.StyledPanel)
self._label.setStyleSheet('background-color: white')
self.set_label()
for r in self._radio:
r[1].clicked.connect(self.set_label)
def set_label(self):
if self._radio[0][1].isChecked():
self._label.setText(self._parent.text_bief() + "\n" +
self._parent.text_time())
else:
self._label.setText(self._parent.text_bief() + "\n" +
self._parent.text_profile())
def setup_envelop_box(self): def setup_envelop_box(self):
self._envelop = [] self._envelop = []
layout = self.find(QVBoxLayout, "verticalLayout_x") layout = self.find(QVBoxLayout, "verticalLayout_x")
+516 -29
View File
@@ -49,7 +49,7 @@ unit = {
class CustomPlot(PamhyrPlot): class CustomPlot(PamhyrPlot):
def __init__(self, x, y, envelop, reach, profile, timestamp, def __init__(self, x, y, envelop, reach, profile, timestamp,
data=None, canvas=None, trad=None, data=None, canvas=None, trad=None, res_id=[0],
toolbar=None, parent=None): toolbar=None, parent=None):
super(CustomPlot, self).__init__( super(CustomPlot, self).__init__(
canvas=canvas, canvas=canvas,
@@ -65,6 +65,8 @@ class CustomPlot(PamhyrPlot):
self._reach = reach self._reach = reach
self._profile = profile self._profile = profile
self._timestamp = timestamp self._timestamp = timestamp
self._current_res_id = res_id[0]
self._parent = parent
logger.debug( logger.debug(
"Create custom plot for: " + "Create custom plot for: " +
@@ -83,6 +85,7 @@ class CustomPlot(PamhyrPlot):
) )
self._axes = {} self._axes = {}
self.lines = {}
def draw_bottom_with_bedload(self, reach): def draw_bottom_with_bedload(self, reach):
self._bedrock = self.sl_compute_bedrock(reach) self._bedrock = self.sl_compute_bedrock(reach)
@@ -130,7 +133,7 @@ class CustomPlot(PamhyrPlot):
""" """
Get SL list for profile p at initial time (initial data) Get SL list for profile p at initial time (initial data)
""" """
t0 = min(list(self.data.get("timestamps"))) t0 = min(list(self.data[self._current_res_id[0]].get("timestamps")))
return map( return map(
lambda p: p.get_ts_key(t0, "sl")[0], lambda p: p.get_ts_key(t0, "sl")[0],
reach.profiles reach.profiles
@@ -145,8 +148,8 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
def get_ts_zmin(self, profile): def get_ts_zmin(self, profile, res_id):
results = self.data results = self.data[res_id]
nt = len(list(results.get("timestamps"))) nt = len(list(results.get("timestamps")))
reach = results.river.reach(self._reach) reach = results.river.reach(self._reach)
berdrock = self.sl_compute_bedrock(reach) berdrock = self.sl_compute_bedrock(reach)
@@ -167,8 +170,11 @@ class CustomPlot(PamhyrPlot):
return ts_z_min return ts_z_min
def _draw_rk(self): def _draw_rk(self):
results = self.data results = self.data[self._current_res_id]
reach = results.river.reach(self._reach) reach = results.river.reach(self._reach)
if self._current_res_id == 2: # compare results
reach1 = self.data[0].river.reach(self._reach)
reach2 = self.data[1].river.reach(self._reach)
rk = reach.geometry.get_rk() rk = reach.geometry.get_rk()
if reach.has_sediment(): if reach.has_sediment():
z_min = self.draw_bottom_with_bedload(reach) z_min = self.draw_bottom_with_bedload(reach)
@@ -204,23 +210,45 @@ class CustomPlot(PamhyrPlot):
self._axes[ax].spines['right'].set_position(('outward', shift)) self._axes[ax].spines['right'].set_position(('outward', shift))
shift += 60 shift += 60
if len(self.lines) != 0:
self.lines = {} self.lines = {}
if "bed_elevation" in self._y: if "bed_elevation" in self._y:
ax = self._axes[unit["bed_elevation"]] ax = self._axes[unit["bed_elevation"]]
if self._current_res_id < 2:
line = ax.plot( line = ax.plot(
rk, z_min, rk, z_min,
color='grey', lw=1., color='grey', lw=1.,
) )
else:
if reach.has_sediment():
z_min1 = self.draw_bottom_with_bedload(reach1)
z_min2 = self.draw_bottom_with_bedload(reach2)
else:
z_min1 = reach1.geometry.get_z_min()
z_min2 = reach2.geometry.get_z_min()
dz = list(
map(
lambda x, y: x - y,
z_min2, z_min2
)
)
line = ax.plot(
rk, dz,
color='grey', lw=1.,
)
self.lines["bed_elevation"] = line self.lines["bed_elevation"] = line
if self._envelop and reach.has_sediment(): if (self._envelop and
reach.has_sediment() and
self._current_res_id < 2):
ax = self._axes[unit["bed_elevation_envelop"]] ax = self._axes[unit["bed_elevation_envelop"]]
e = list( e = list(
map( map(
lambda p: max(self.get_ts_zmin(p)), lambda p: max(self.get_ts_zmin(p, res_id)),
range(len(reach)) range(len(reach))
) )
) )
@@ -233,7 +261,7 @@ class CustomPlot(PamhyrPlot):
e = list( e = list(
map( map(
lambda p: min(self.get_ts_zmin(p)), lambda p: min(self.get_ts_zmin(p, res_id)),
range(len(reach)) range(len(reach))
) )
) )
@@ -253,7 +281,7 @@ class CustomPlot(PamhyrPlot):
) )
self.lines["water_elevation"] = line self.lines["water_elevation"] = line
if "bed_elevation" in self._y: if "bed_elevation" in self._y and self._current_res_id < 2:
self.fill = ax.fill_between( self.fill = ax.fill_between(
rk, z_min, z, rk, z_min, z,
color='blue', alpha=0.5, interpolate=True color='blue', alpha=0.5, interpolate=True
@@ -370,6 +398,7 @@ class CustomPlot(PamhyrPlot):
if "depth" in self._y: if "depth" in self._y:
ax = self._axes[unit["depth"]] ax = self._axes[unit["depth"]]
if self._current_res_id < 2:
d = list( d = list(
map( map(
lambda p: p.geometry.max_water_depth( lambda p: p.geometry.max_water_depth(
@@ -377,13 +406,35 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
line = ax.plot( line = ax.plot(
rk, d, rk, d,
color='brown', lw=1., color='brown', lw=1.,
) )
self.lines["depth"] = line self.lines["depth"] = line
if self._envelop: if self._envelop and self._current_res_id < 2:
ax = self._axes[unit["depth_envelop"]] ax = self._axes[unit["depth_envelop"]]
@@ -416,6 +467,7 @@ class CustomPlot(PamhyrPlot):
if "mean_depth" in self._y: if "mean_depth" in self._y:
ax = self._axes[unit["mean_depth"]] ax = self._axes[unit["mean_depth"]]
if self._current_res_id < 2:
d = list( d = list(
map( map(
lambda p: p.geometry.mean_water_depth( lambda p: p.geometry.mean_water_depth(
@@ -423,6 +475,27 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
line = ax.plot( line = ax.plot(
rk, d, rk, d,
@@ -433,6 +506,7 @@ class CustomPlot(PamhyrPlot):
if "froude" in self._y: if "froude" in self._y:
ax = self._axes[unit["froude"]] ax = self._axes[unit["froude"]]
if self._current_res_id < 2:
fr = list( fr = list(
map( map(
lambda p: lambda p:
@@ -446,6 +520,39 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
fr1 = list(
map(
lambda p:
p.get_ts_key(self._timestamp, "V") /
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"))
)),
reach1.profiles
)
)
fr2 = list(
map(
lambda p:
p.get_ts_key(self._timestamp, "V") /
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"))
)),
reach2.profiles
)
)
fr = list(
map(
lambda x, y: x - y,
fr1, fr2
)
)
line = ax.plot( line = ax.plot(
rk, fr, color='black', linestyle='--', lw=1., rk, fr, color='black', linestyle='--', lw=1.,
@@ -455,6 +562,7 @@ class CustomPlot(PamhyrPlot):
if "wet_area" in self._y: if "wet_area" in self._y:
ax = self._axes[unit["wet_area"]] ax = self._axes[unit["wet_area"]]
if self._current_res_id < 2:
d = list( d = list(
map( map(
lambda p: p.geometry.wet_area( lambda p: p.geometry.wet_area(
@@ -462,6 +570,27 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
line = ax.plot( line = ax.plot(
rk, d, rk, d,
@@ -479,8 +608,12 @@ class CustomPlot(PamhyrPlot):
self.canvas.axes.legend(lns, labs, loc="best") self.canvas.axes.legend(lns, labs, loc="best")
def _redraw_rk(self): def _redraw_rk(self):
results = self.data results = self.data[self._current_res_id]
reach = results.river.reach(self._reach) reach = results.river.reach(self._reach)
if self._current_res_id == 2: # compare results
reach1 = self.data[0].river.reach(self._reach)
reach2 = self.data[1].river.reach(self._reach)
rk = reach.geometry.get_rk() rk = reach.geometry.get_rk()
z_min = reach.geometry.get_z_min() z_min = reach.geometry.get_z_min()
if reach.has_sediment(): if reach.has_sediment():
@@ -507,12 +640,27 @@ class CustomPlot(PamhyrPlot):
) )
) )
if "bed_elevation" in self._y: if "bed_elevation" in self._y:
self.lines["bed_elevation"][0].set_ydata(z_min) if self._current_res_id < 2:
dz = z_min
else:
if reach.has_sediment():
z_min1 = self.draw_bottom_with_bedload(reach1)
z_min2 = self.draw_bottom_with_bedload(reach2)
else:
z_min1 = reach1.geometry.get_z_min()
z_min2 = reach2.geometry.get_z_min()
dz = list(
map(
lambda x, y: x - y,
z_min2, z_min2
)
)
self.lines["bed_elevation"][0].set_ydata(dz)
if "water_elevation" in self._y: if "water_elevation" in self._y:
self.lines["water_elevation"][0].set_ydata(z) self.lines["water_elevation"][0].set_ydata(z)
if "bed_elevation" in self._y: if "bed_elevation" in self._y and self._current_res_id < 2:
ax = self._axes[unit["water_elevation"]] ax = self._axes[unit["water_elevation"]]
self.fill.remove() self.fill.remove()
self.fill = ax.fill_between( self.fill = ax.fill_between(
@@ -527,6 +675,7 @@ class CustomPlot(PamhyrPlot):
self.lines["velocity"][0].set_ydata(v) self.lines["velocity"][0].set_ydata(v)
if "depth" in self._y: if "depth" in self._y:
if self._current_res_id < 2:
d = list( d = list(
map( map(
lambda p: p.geometry.max_water_depth( lambda p: p.geometry.max_water_depth(
@@ -534,9 +683,32 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
self.lines["depth"][0].set_ydata(d) self.lines["depth"][0].set_ydata(d)
if "mean_depth" in self._y: if "mean_depth" in self._y:
if self._current_res_id < 2:
d = list( d = list(
map( map(
lambda p: p.geometry.mean_water_depth( lambda p: p.geometry.mean_water_depth(
@@ -544,9 +716,32 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
self.lines["mean_depth"][0].set_ydata(d) self.lines["mean_depth"][0].set_ydata(d)
if "froude" in self._y: if "froude" in self._y:
if self._current_res_id < 2:
fr = list( fr = list(
map( map(
lambda p: lambda p:
@@ -560,9 +755,44 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
fr1 = list(
map(
lambda p:
p.get_ts_key(self._timestamp, "V") /
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"))
)),
reach1.profiles
)
)
fr2 = list(
map(
lambda p:
p.get_ts_key(self._timestamp, "V") /
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"))
)),
reach2.profiles
)
)
fr = list(
map(
lambda x, y: x - y,
fr1, fr2
)
)
self.lines["froude"][0].set_ydata(fr) self.lines["froude"][0].set_ydata(fr)
if "wet_area" in self._y: if "wet_area" in self._y:
if self._current_res_id < 2:
d = list( d = list(
map( map(
lambda p: p.geometry.wet_area( lambda p: p.geometry.wet_area(
@@ -570,6 +800,28 @@ class CustomPlot(PamhyrPlot):
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
self.lines["wet_area"][0].set_ydata(d) self.lines["wet_area"][0].set_ydata(d)
def _customize_x_axes_time(self, ts, mode="time"): def _customize_x_axes_time(self, ts, mode="time"):
@@ -611,9 +863,11 @@ class CustomPlot(PamhyrPlot):
) )
self.canvas.axes.set_xticks(ticks=fx, labels=xt, rotation=45) self.canvas.axes.set_xticks(ticks=fx, labels=xt, rotation=45)
self.canvas.axes.relim(visible_only=True)
self.canvas.axes.autoscale_view()
def _draw_time(self): def _draw_time(self):
results = self.data results = self.data[self._current_res_id]
reach = results.river.reach(self._reach) reach = results.river.reach(self._reach)
profile = reach.profile(self._profile) profile = reach.profile(self._profile)
@@ -627,15 +881,29 @@ class CustomPlot(PamhyrPlot):
self._axes[ax].spines['right'].set_position(('outward', shift)) self._axes[ax].spines['right'].set_position(('outward', shift))
shift += 60 shift += 60
ts = list(results.get("timestamps")) ts = self._parent._timestamps
ts.sort()
if self._current_res_id == 2: # compare results
reach1 = self.data[0].river.reach(self._reach)
reach2 = self.data[1].river.reach(self._reach)
profile1 = reach1.profile(self._profile)
profile2 = reach2.profile(self._profile)
q1 = profile1.get_key("Q")
z1 = profile1.get_key("Z")
v1 = profile1.get_key("V")
q2 = profile2.get_key("Q")
z2 = profile2.get_key("Z")
v2 = profile2.get_key("V")
q = profile.get_key("Q") q = profile.get_key("Q")
z = profile.get_key("Z") z = profile.get_key("Z")
v = profile.get_key("V") v = profile.get_key("V")
z_min = profile.geometry.z_min() z_min = profile.geometry.z_min()
if self._current_res_id < 2:
if reach.has_sediment(): if reach.has_sediment():
ts_z_min = self.get_ts_zmin(self._profile) ts_z_min = self.get_ts_zmin(self._profile, res_id)
else: else:
ts_z_min = list( ts_z_min = list(
map( map(
@@ -650,6 +918,26 @@ class CustomPlot(PamhyrPlot):
ax = self._axes[unit["bed_elevation"]] ax = self._axes[unit["bed_elevation"]]
if self._current_res_id == 2:
if reach.has_sediment():
ts_z_min1 = self.get_ts_zmin(self._profile1, 0)
ts_z_min2 = self.get_ts_zmin(self._profile2, 1)
ts_z_min = list(
map(
lambda x, y: x - y,
ts_z_min1, ts_z_min2
)
)
else:
z_min1 = profile1.geometry.z_min()
z_min2 = profile2.geometry.z_min()
ts_z_min = list(
map(
lambda ts: z_min1 - z_min2,
ts
)
)
line = ax.plot( line = ax.plot(
ts, ts_z_min, ts, ts_z_min,
color='grey', lw=1. color='grey', lw=1.
@@ -665,7 +953,7 @@ class CustomPlot(PamhyrPlot):
) )
self.lines["water_elevation"] = line self.lines["water_elevation"] = line
if "bed_elevation" in self._y: if "bed_elevation" in self._y and self._current_res_id < 2:
self.fill = ax.fill_between( self.fill = ax.fill_between(
ts, ts_z_min, z, ts, ts_z_min, z,
@@ -694,9 +982,23 @@ class CustomPlot(PamhyrPlot):
if "depth" in self._y: if "depth" in self._y:
ax = self._axes[unit["depth"]] ax = self._axes[unit["depth"]]
if self._current_res_id < 2:
d = list( d = list(
map(lambda z: profile.geometry.max_water_depth(z), z) map(lambda z: profile.geometry.max_water_depth(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.max_water_depth(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.max_water_depth(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
line = ax.plot( line = ax.plot(
ts, d, ts, d,
@@ -707,9 +1009,23 @@ class CustomPlot(PamhyrPlot):
if "mean_depth" in self._y: if "mean_depth" in self._y:
ax = self._axes[unit["mean_depth"]] ax = self._axes[unit["mean_depth"]]
if self._current_res_id < 2:
d = list( d = list(
map(lambda z: profile.geometry.mean_water_depth(z), z) map(lambda z: profile.geometry.mean_water_depth(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.mean_water_depth(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.mean_water_depth(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
line = ax.plot( line = ax.plot(
ts, d, ts, d,
@@ -720,6 +1036,7 @@ class CustomPlot(PamhyrPlot):
if "froude" in self._y: if "froude" in self._y:
ax = self._axes[unit["froude"]] ax = self._axes[unit["froude"]]
if self._current_res_id < 2:
d = list( d = list(
map(lambda z, v: map(lambda z, v:
v / v /
@@ -728,6 +1045,29 @@ class CustomPlot(PamhyrPlot):
profile.geometry.wet_width(z)) profile.geometry.wet_width(z))
), z, v) ), z, v)
) )
else:
d1 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
profile1.geometry.wet_area(z) /
profile1.geometry.wet_width(z))
), z1, v1)
)
d2 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
profile2.geometry.wet_area(z) /
profile2.geometry.wet_width(z))
), z2, v2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
line = ax.plot( line = ax.plot(
ts, d, color='black', linestyle='--', lw=1., ts, d, color='black', linestyle='--', lw=1.,
@@ -737,9 +1077,23 @@ class CustomPlot(PamhyrPlot):
if "wet_area" in self._y: if "wet_area" in self._y:
ax = self._axes[unit["wet_area"]] ax = self._axes[unit["wet_area"]]
if self._current_res_id < 2:
d = list( d = list(
map(lambda z: profile.geometry.wet_area(z), z) map(lambda z: profile.geometry.wet_area(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.wet_area(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.wet_area(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
line = ax.plot( line = ax.plot(
ts, d, color='blue', linestyle='--', lw=1., ts, d, color='blue', linestyle='--', lw=1.,
@@ -759,15 +1113,30 @@ class CustomPlot(PamhyrPlot):
def _redraw_time(self): def _redraw_time(self):
results = self.data results = self.data[self._current_res_id]
reach = results.river.reach(self._reach) reach = results.river.reach(self._reach)
profile = reach.profile(self._profile) profile = reach.profile(self._profile)
ts = list(results.get("timestamps")) ts = list(results.get("timestamps"))
ts.sort() ts.sort()
if self._current_res_id == 2: # compare results
reach1 = self.data[0].river.reach(self._reach)
reach2 = self.data[1].river.reach(self._reach)
profile1 = reach1.profile(self._profile)
profile2 = reach2.profile(self._profile)
q1 = profile1.get_key("Q")
z1 = profile1.get_key("Z")
v1 = profile1.get_key("V")
q2 = profile2.get_key("Q")
z2 = profile2.get_key("Z")
v2 = profile2.get_key("V")
q = profile.get_key("Q") q = profile.get_key("Q")
z = profile.get_key("Z") z = profile.get_key("Z")
v = profile.get_key("V") v = profile.get_key("V")
if self._current_res_id < 2:
if reach.has_sediment(): if reach.has_sediment():
ts_z_min = self.get_ts_zmin(self._profile) ts_z_min = self.get_ts_zmin(self._profile)
else: else:
@@ -779,12 +1148,31 @@ class CustomPlot(PamhyrPlot):
) )
) )
if "bed_elevation" in self._y: if "bed_elevation" in self._y:
if self._current_res_id == 2:
if reach.has_sediment():
ts_z_min1 = self.get_ts_zmin(self._profile1, 0)
ts_z_min2 = self.get_ts_zmin(self._profile2, 1)
ts_z_min = list(
map(
lambda x, y: x - y,
ts_z_min1, ts_z_min2
)
)
else:
z_min1 = profile1.geometry.z_min()
z_min2 = profile2.geometry.z_min()
ts_z_min = list(
map(
lambda ts: z_min1 - z_min2,
ts
)
)
self.lines["bed_elevation"][0].set_ydata(ts_z_min) self.lines["bed_elevation"][0].set_ydata(ts_z_min)
if "water_elevation" in self._y: if "water_elevation" in self._y:
self.lines["water_elevation"][0].set_ydata(z) self.lines["water_elevation"][0].set_ydata(z)
if "bed_elevation" in self._y: if "bed_elevation" in self._y and self._current_res_id < 2:
ax = self._axes[unit["bed_elevation"]] ax = self._axes[unit["bed_elevation"]]
self.fill.remove() self.fill.remove()
self.fill = ax.fill_between( self.fill = ax.fill_between(
@@ -799,18 +1187,49 @@ class CustomPlot(PamhyrPlot):
self.lines["velocity"][0].set_ydata(v) self.lines["velocity"][0].set_ydata(v)
if "depth" in self._y: if "depth" in self._y:
if self._current_res_id < 2:
d = list( d = list(
map(lambda z: profile.geometry.max_water_depth(z), z) map(lambda z: profile.geometry.max_water_depth(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.max_water_depth(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.max_water_depth(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
self.lines["depth"][0].set_ydata(d) self.lines["depth"][0].set_ydata(d)
if "mean_depth" in self._y: if "mean_depth" in self._y:
if self._current_res_id < 2:
d = list( d = list(
map(lambda z: profile.geometry.mean_water_depth(z), z) map(lambda z: profile.geometry.mean_water_depth(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.mean_water_depth(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.mean_water_depth(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
self.lines["mean_depth"][0].set_ydata(d) self.lines["mean_depth"][0].set_ydata(d)
if "froude" in self._y: if "froude" in self._y:
if self._current_res_id < 2:
d = list( d = list(
map(lambda z, v: map(lambda z, v:
v / v /
@@ -819,16 +1238,57 @@ class CustomPlot(PamhyrPlot):
profile.geometry.wet_width(z)) profile.geometry.wet_width(z))
), z, v) ), z, v)
) )
else:
d1 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
profile1.geometry.wet_area(z) /
profile1.geometry.wet_width(z))
), z1, v1)
)
d2 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
profile2.geometry.wet_area(z) /
profile2.geometry.wet_width(z))
), z2, v2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
self.lines["froude"][0].set_ydata(d) self.lines["froude"][0].set_ydata(d)
if "wet_area" in self._y: if "wet_area" in self._y:
if self._current_res_id < 2:
d = list( d = list(
map(lambda z: profile.geometry.wet_area(z), z) map(lambda z: profile.geometry.wet_area(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.wet_area(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.wet_area(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
self.lines["wet_area"][0].set_ydata(d) self.lines["wet_area"][0].set_ydata(d)
self._current.set_visible(False)
self.canvas.axes.relim(visible_only=True) self.canvas.axes.relim(visible_only=True)
self.canvas.axes.autoscale_view() self.canvas.axes.autoscale_view()
self._current.set_visible(True)
def draw(self): def draw(self):
self.draw_static() self.draw_static()
@@ -847,35 +1307,58 @@ class CustomPlot(PamhyrPlot):
if self.data is None: if self.data is None:
return return
self._y_axes = sorted(
set(
map(
lambda y: unit[y],
self._y
)
)
)
self.canvas.axes.set_xlabel( self.canvas.axes.set_xlabel(
self._trad[self._x], self._trad[self._x],
color='black', fontsize=10 color='black', fontsize=10
) )
if self._current_res_id < 2:
self.canvas.axes.set_ylabel( self.canvas.axes.set_ylabel(
self._trad[self._y_axes[0]], self._trad[self._y_axes[0]],
color='black', fontsize=10 color='black', fontsize=10
) )
else:
self.canvas.axes.set_ylabel(
"Δ " + self._trad[self._y_axes[0]],
color='black', fontsize=10
)
self._axes[self._y_axes[0]] = self.canvas.axes self._axes[self._y_axes[0]] = self.canvas.axes
if len(self.lines) != 0:
self.lines.clear()
for axes in self._y_axes[1:]: for axes in self._y_axes[1:]:
if axes in self._axes: if axes in self._axes.keys():
self._axes[axes].clear() for ll in self._axes[axes].lines:
continue ll.remove()
else:
ax_new = self.canvas.axes.twinx() ax_new = self.canvas.axes.twinx()
ax_new.set_ylabel( self._axes[axes] = ax_new
if self._current_res_id < 2:
self._axes[axes].set_ylabel(
self._trad[axes], self._trad[axes],
color='black', fontsize=10 color='black', fontsize=10
) )
self._axes[axes] = ax_new else:
self._axes[axes].set_ylabel(
"Δ " + self._trad[axes],
color='black', fontsize=10
)
if self._x == "rk": if self._x == "rk":
self._draw_rk() self._draw_rk()
elif self._x == "time": elif self._x == "time":
self._draw_time() self._draw_time()
if self._x == "rk": if self._x == "rk":
reach = self.data.river.reach(self._reach) reach = self.data[self._current_res_id].river.reach(self._reach)
profile = reach.profile(self._profile) profile = reach.profile(self._profile)
x = profile.rk x = profile.rk
elif self._x == "time": elif self._x == "time":
@@ -905,7 +1388,7 @@ class CustomPlot(PamhyrPlot):
self._reach = reach_id self._reach = reach_id
self._profile = 0 self._profile = 0
self.update() self.draw()
def set_profile(self, profile_id): def set_profile(self, profile_id):
self._profile = profile_id self._profile = profile_id
@@ -915,6 +1398,10 @@ class CustomPlot(PamhyrPlot):
else: else:
self.draw_current() self.draw_current()
def set_result(self, res_id):
self._current_res_id = res_id[0]
self.draw()
def set_timestamp(self, timestamp): def set_timestamp(self, timestamp):
self._timestamp = timestamp self._timestamp = timestamp
@@ -925,7 +1412,7 @@ class CustomPlot(PamhyrPlot):
def draw_current(self): def draw_current(self):
if self._x == "rk": if self._x == "rk":
reach = self.data.river.reach(self._reach) reach = self.data[self._current_res_id].river.reach(self._reach)
profile = reach.profile(self._profile) profile = reach.profile(self._profile)
x = profile.rk x = profile.rk
elif self._x == "time": elif self._x == "time":
+95 -8
View File
@@ -29,7 +29,7 @@ _translate = QCoreApplication.translate
class PlotAC(PamhyrPlot): class PlotAC(PamhyrPlot):
def __init__(self, canvas=None, trad=None, toolbar=None, def __init__(self, canvas=None, trad=None, toolbar=None,
results=None, reach_id=0, profile_id=0, results=None, reach_id=0, profile_id=[0], res_id=[0],
parent=None): parent=None):
super(PlotAC, self).__init__( super(PlotAC, self).__init__(
canvas=canvas, canvas=canvas,
@@ -39,9 +39,11 @@ class PlotAC(PamhyrPlot):
parent=parent parent=parent
) )
self._current_timestamp = max(results.get("timestamps"))
self._current_reach_id = reach_id self._current_reach_id = reach_id
self._current_profile_id = profile_id self._current_profile_id = profile_id
self._current_res_id = res_id[0]
self._timestamps = parent._timestamps
self._current_timestamp = max(self._timestamps)
self.label_x = self._trad['x'] self.label_x = self._trad['x']
self.label_y = self._trad["unit_elevation"] self.label_y = self._trad["unit_elevation"]
@@ -62,7 +64,7 @@ class PlotAC(PamhyrPlot):
@results.setter @results.setter
def results(self, results): def results(self, results):
self.data = results self.data = results
self._current_timestamp = max(results.get("timestamps")) self._current_timestamp = max(self._timestamps)
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
@@ -71,12 +73,17 @@ class PlotAC(PamhyrPlot):
if self.results is None: if self.results is None:
return return
reach = self.results.river.reach(self._current_reach_id) if self.results[self._current_res_id] is None:
return
results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
profile = reach.profile(self._current_profile_id) profile = reach.profile(self._current_profile_id)
self.draw_profile(reach, profile) self.draw_profile(reach, profile)
self.draw_water_elevation(reach, profile) self.draw_water_elevation(reach, profile)
self.draw_water_elevation_max(reach, profile) self.draw_water_elevation_max(reach, profile)
self.draw_gl()
self.enable_legend() self.enable_legend()
@@ -109,7 +116,7 @@ class PlotAC(PamhyrPlot):
self.collection = self.canvas.axes.fill_between( self.collection = self.canvas.axes.fill_between(
x, z, water_z, x, z, water_z,
where=z <= water_z, where=list(map(lambda x: x <= water_z, z)),
color=self.color_plot_river_water_zone, color=self.color_plot_river_water_zone,
alpha=0.7, interpolate=True alpha=0.7, interpolate=True
) )
@@ -128,6 +135,72 @@ class PlotAC(PamhyrPlot):
lw=1., color=self.color_plot_river_water, lw=1., color=self.color_plot_river_water,
) )
def draw_gl(self):
results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
profile = reach.profile(self._current_profile_id)
if profile is None:
return
station = profile.geometry.get_station()
elevation = profile.geometry.z()
gl = profile.geometry.names()
self.annotation = []
self.cgl, self.igl = reach.geometry.compute_guidelines()
# we make sure that the lines are in the left-to-right order
lcomplete = [
x.name
for x in profile.geometry.named_points()
if x.name in self.cgl
]
lincomplete = [
x.name
for x in profile.geometry.named_points()
if x.name in self.igl
]
self.color_complete_gl = self.colors
self.color_incomplete_gl = 2 * ["grey"]
x_gl_complete = []
y_gl_complete = []
x_gl_incomplete = []
y_gl_incomplete = []
for i, txt in enumerate(gl):
if txt == "":
continue
if txt.strip() in self.cgl:
color = self.color_complete_gl[
lcomplete.index(txt) % len(self.color_complete_gl)
]
else:
color = self.color_incomplete_gl[
lincomplete.index(txt) % len(self.color_incomplete_gl)
]
annotation = self.canvas.axes.annotate(
txt, (station[i], elevation[i]),
horizontalalignment='left',
verticalalignment='top',
annotation_clip=True,
fontsize=11, color=color
)
annotation.set_position((station[i] + 0., elevation[i] + 0.))
self.annotation.append(annotation)
if txt.strip() in self.cgl:
x_gl_complete.append(station[i])
y_gl_complete.append(elevation[i])
else:
x_gl_incomplete.append(station[i])
y_gl_incomplete.append(elevation[i])
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
@@ -137,10 +210,15 @@ class PlotAC(PamhyrPlot):
self._current_profile_id = profile_id self._current_profile_id = profile_id
self.update() self.update()
def set_result(self, res_id):
self._current_res_id = res_id[0]
self.update()
def set_timestamp(self, timestamp): def set_timestamp(self, timestamp):
self._current_timestamp = timestamp self._current_timestamp = timestamp
reach = self.results.river.reach(self._current_reach_id) reach = self.results[self._current_res_id].river.reach(
self._current_reach_id)
profile = reach.profile(self._current_profile_id) profile = reach.profile(self._current_profile_id)
x = profile.geometry.get_station() x = profile.geometry.get_station()
z = profile.geometry.z() z = profile.geometry.z()
@@ -152,7 +230,8 @@ class PlotAC(PamhyrPlot):
if not self._init: if not self._init:
self.draw() self.draw()
reach = self.results.river.reach(self._current_reach_id) reach = self.results[self._current_res_id].river.reach(
self._current_reach_id)
profile = reach.profile(self._current_profile_id) profile = reach.profile(self._current_profile_id)
x = profile.geometry.get_station() x = profile.geometry.get_station()
z = profile.geometry.z() z = profile.geometry.z()
@@ -160,9 +239,17 @@ class PlotAC(PamhyrPlot):
self.update_river_bottom(reach, profile, x, z) self.update_river_bottom(reach, profile, x, z)
self.update_water(reach, profile, x, z) self.update_water(reach, profile, x, z)
self.update_water_max(reach, profile, x, z) self.update_water_max(reach, profile, x, z)
self.update_gl()
self.update_idle() self.update_idle()
def update_gl(self):
for a in self.annotation:
a.remove()
self.annotation[:] = []
self.draw_gl()
def update_river_bottom(self, reach, profile, x, z): def update_river_bottom(self, reach, profile, x, z):
self.line_rk.set_data(x, z) self.line_rk.set_data(x, z)
@@ -176,7 +263,7 @@ class PlotAC(PamhyrPlot):
self.collection.remove() self.collection.remove()
self.collection = self.canvas.axes.fill_between( self.collection = self.canvas.axes.fill_between(
x, z, water_z, x, z, water_z,
where=z <= water_z, where=list(map(lambda x: x <= water_z, z)),
color=self.color_plot_river_water_zone, color=self.color_plot_river_water_zone,
alpha=0.7, interpolate=True alpha=0.7, interpolate=True
) )
+91 -37
View File
@@ -35,7 +35,7 @@ logger = logging.getLogger()
class PlotH(PamhyrPlot): class PlotH(PamhyrPlot):
def __init__(self, canvas=None, trad=None, toolbar=None, def __init__(self, canvas=None, trad=None, toolbar=None,
results=None, reach_id=0, profile_id=0, results=None, reach_id=0, profile_id=[0], res_id=[0],
parent=None): parent=None):
super(PlotH, self).__init__( super(PlotH, self).__init__(
canvas=canvas, canvas=canvas,
@@ -47,9 +47,11 @@ class PlotH(PamhyrPlot):
self._mode = "time" self._mode = "time"
self._current_timestamp = max(results.get("timestamps"))
self._current_reach_id = reach_id self._current_reach_id = reach_id
self._current_profile_id = profile_id self._current_profile_id = profile_id
self._current_res_id = res_id
self._timestamps = parent._timestamps
self._current_timestamp = max(self._timestamps)
self.label_x = self._trad["unit_time_s"] self.label_x = self._trad["unit_time_s"]
self.label_y = self._trad["unit_discharge"] self.label_y = self._trad["unit_discharge"]
@@ -60,8 +62,11 @@ class PlotH(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = False # self._auto_relim_update = False
self._autoscale_update = False # self._autoscale_update = False
self._line_max = []
self._line = []
@property @property
def results(self): def results(self):
@@ -70,47 +75,81 @@ class PlotH(PamhyrPlot):
@results.setter @results.setter
def results(self, results): def results(self, results):
self.data = results self.data = results
self._current_timestamp = max(results.get("timestamps")) self._current_timestamp = max(self._timestamps)
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
self.init_axes() self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
if self.results is None: if self.results is None:
return return
reach = self.results.river.reach(self._current_reach_id) if len(self._current_res_id) < 1:
profile = reach.profile(self._current_profile_id) return
if len(self._current_res_id) == 1:
self.draw_max(self._current_res_id[0])
for res_id in self._current_res_id:
if self.results[res_id] is None:
continue
results = self.results[res_id]
reach = results.river.reach(self._current_reach_id)
if reach.geometry.number_profiles == 0: if reach.geometry.number_profiles == 0:
self._init = False self._init = False
return return
self.draw_max(reach) self.draw_data(res_id)
self.draw_data(reach, profile)
self.draw_current() self.canvas.axes.set_xlabel(
self._trad["unit_time_s"],
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
self._trad["unit_discharge"],
color='black', fontsize=10
)
self.set_ticks_time_formater() self.set_ticks_time_formater()
self.enable_legend() self.enable_legend()
self.idle() self.canvas.draw_idle()
self.update_current() self.update_idle
self.draw_current()
self._init = True self._init = True
def draw_data(self, reach, profile): def draw_data(self, res_id):
self.ts = list(self.results.get("timestamps"))
self.ts.sort()
x = self.ts results = self.results[res_id]
reach = results.river.reach(self._current_reach_id)
for i, p in enumerate(self._current_profile_id):
profile = reach.profile(p)
x = self._timestamps
y = profile.get_key("Q") y = profile.get_key("Q")
self._line, = self.canvas.axes.plot( if res_id == 2:
label = "Δ " + self.label_discharge + f" {profile.name}"
else:
label = self.label_discharge + f" {profile.name}"
if len(self._current_res_id) > 1:
if res_id != 2:
label += f" ({results._solver.name})"
line, = self.canvas.axes.plot(
x, y, x, y,
label=self.label_discharge, label=label,
color=self.color_plot, color=self.colors[res_id + 1],
linestyle=self.linestyle[i % len(self.linestyle)],
**self.plot_default_kargs **self.plot_default_kargs
) )
self._line.append(line)
def draw_current(self): def draw_current(self):
self._current, = self.canvas.axes.plot( self._current, = self.canvas.axes.plot(
@@ -122,12 +161,22 @@ class PlotH(PamhyrPlot):
lw=1., lw=1.,
) )
def draw_max(self, reach): def draw_max(self, res_id):
self.ts = list(self.results.get("timestamps")) results = self.results[res_id]
self.ts.sort() reach = results.river.reach(self._current_reach_id)
x = self.ts if reach.geometry.number_profiles == 0:
self._init = False
return
x = self._timestamps
y = [] y = []
if res_id == 2:
label = "Δ " + self.label_discharge_max
else:
label = self.label_discharge_max
for ts in x: for ts in x:
ts_y = -9999 ts_y = -9999
for profile in reach.profiles: for profile in reach.profiles:
@@ -135,22 +184,28 @@ class PlotH(PamhyrPlot):
ts_y = max(ts_y, q) ts_y = max(ts_y, q)
y.append(ts_y) y.append(ts_y)
self._line_max, = self.canvas.axes.plot( m, = self.canvas.axes.plot(
x, y, x, y,
label=self.label_discharge_max, label=label,
color=self.color_plot_highlight, color=self.color_plot_highlight,
linestyle='dotted', linestyle='dotted',
**self.plot_default_kargs **self.plot_default_kargs
) )
self._line_max.append(m)
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.draw()
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.update() # self.update()
self.draw()
def set_result(self, res_id):
self._current_res_id = res_id
self.draw()
def set_timestamp(self, timestamp): def set_timestamp(self, timestamp):
self._current_timestamp = timestamp self._current_timestamp = timestamp
@@ -162,24 +217,23 @@ class PlotH(PamhyrPlot):
self.draw() self.draw()
self.update_data() self.update_data()
self.update_current()
self.update_idle() self.update_idle()
def update_data(self): def update_data(self):
reach = self.results.river.reach(self._current_reach_id) for res, res_id in enumerate(self._current_res_id):
results = self.results[res_id]
reach = results.river.reach(self._current_reach_id)
profile = reach.profile(self._current_profile_id) profile = reach.profile(self._current_profile_id)
x = self.ts x = self._timestamps
y = profile.get_key("Q") y = profile.get_key("Q")
self._line.set_data(x, y) self._line[res].set_data(x, y)
self._current.set_data(
[self._current_timestamp, self._current_timestamp],
self.canvas.axes.get_ylim()
)
def update_current(self): def update_current(self):
y = self._current.get_ydata()
self._current.set_data( self._current.set_data(
[self._current_timestamp, self._current_timestamp], [self._current_timestamp, self._current_timestamp],
self.canvas.axes.get_ylim() y
) )
+29 -10
View File
@@ -32,7 +32,7 @@ logger = logging.getLogger()
class PlotRKC(PamhyrPlot): class PlotRKC(PamhyrPlot):
def __init__(self, canvas=None, trad=None, toolbar=None, def __init__(self, canvas=None, trad=None, toolbar=None,
results=None, reach_id=0, profile_id=0, results=None, reach_id=0, profile_id=0, res_id=0,
parent=None): parent=None):
super(PlotRKC, self).__init__( super(PlotRKC, self).__init__(
canvas=canvas, canvas=canvas,
@@ -42,10 +42,11 @@ class PlotRKC(PamhyrPlot):
parent=parent parent=parent
) )
self._timestamps = results.get("timestamps")
self._current_timestamp = max(self._timestamps)
self._current_reach_id = reach_id self._current_reach_id = reach_id
self._current_profile_id = profile_id self._current_profile_id = profile_id
self._current_res_id = res_id[0]
self._timestamps = parent._timestamps
self._current_timestamp = max(self._timestamps)
self.label_x = self._trad["unit_rk"] self.label_x = self._trad["unit_rk"]
self.label_y = self._trad["unit_elevation"] self.label_y = self._trad["unit_elevation"]
@@ -56,6 +57,9 @@ class PlotRKC(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = True
@property @property
def results(self): def results(self):
return self.data return self.data
@@ -63,7 +67,7 @@ class PlotRKC(PamhyrPlot):
@results.setter @results.setter
def results(self, results): def results(self, results):
self.data = results self.data = results
self._current_timestamp = max(results.get("timestamps")) self._current_timestamp = max(self._timestamps)
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
@@ -72,7 +76,11 @@ class PlotRKC(PamhyrPlot):
if self.results is None: if self.results is None:
return return
reach = self.results.river.reach(self._current_reach_id) if self.results[self._current_res_id] is None:
return
results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
self.draw_bottom(reach) self.draw_bottom(reach)
self.draw_water_elevation(reach) self.draw_water_elevation(reach)
@@ -107,11 +115,12 @@ class PlotRKC(PamhyrPlot):
self._river_bottom = z self._river_bottom = z
def draw_profiles_hs(self, reach): def draw_profiles_hs(self, reach):
results = self.results[self._current_res_id]
lhs = filter( lhs = filter(
lambda hs: hs._input_reach.reach is reach.geometry, lambda hs: hs._input_reach.reach is reach.geometry,
filter( filter(
lambda hs: hs._input_reach is not None, lambda hs: hs._input_reach is not None,
self.results.study.river.hydraulic_structures.lst results.study.river.hydraulic_structures.lst
) )
) )
@@ -223,6 +232,7 @@ class PlotRKC(PamhyrPlot):
self.water_fill = self.canvas.axes.fill_between( self.water_fill = self.canvas.axes.fill_between(
rk, self._river_bottom, water_z, rk, self._river_bottom, water_z,
where=[b <= z for b, z in zip(self._river_bottom, water_z)],
color=self.color_plot_river_water_zone, color=self.color_plot_river_water_zone,
alpha=0.7, alpha=0.7,
interpolate=True interpolate=True
@@ -312,6 +322,10 @@ class PlotRKC(PamhyrPlot):
self._current_profile_id = profile_id self._current_profile_id = profile_id
self.update_current() self.update_current()
def set_result(self, res_id):
self._current_res_id = res_id[0]
self.update()
def set_timestamp(self, timestamp): def set_timestamp(self, timestamp):
self._current_timestamp = timestamp self._current_timestamp = timestamp
self.update() self.update()
@@ -320,7 +334,8 @@ class PlotRKC(PamhyrPlot):
if not self._init: if not self._init:
self.draw() self.draw()
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
if reach.has_sediment(): if reach.has_sediment():
self.update_bottom_with_bedload() self.update_bottom_with_bedload()
@@ -329,7 +344,8 @@ class PlotRKC(PamhyrPlot):
self.update_idle() self.update_idle()
def update_water_elevation(self): def update_water_elevation(self):
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
rk = reach.geometry.get_rk() rk = reach.geometry.get_rk()
z_min = reach.geometry.get_z_min() z_min = reach.geometry.get_z_min()
@@ -349,12 +365,14 @@ class PlotRKC(PamhyrPlot):
self.water_fill.remove() self.water_fill.remove()
self.water_fill = self.canvas.axes.fill_between( self.water_fill = self.canvas.axes.fill_between(
rk, self._river_bottom, water_z, rk, self._river_bottom, water_z,
where=[rb <= wz for rb, wz in zip(self._river_bottom, water_z)],
color=self.color_plot_river_water_zone, color=self.color_plot_river_water_zone,
alpha=0.7, interpolate=True alpha=0.7, interpolate=True
) )
def update_current(self): def update_current(self):
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
rk = reach.geometry.get_rk() rk = reach.geometry.get_rk()
z_min = reach.geometry.get_z_min() z_min = reach.geometry.get_z_min()
z_max = reach.geometry.get_z_max() z_max = reach.geometry.get_z_max()
@@ -367,7 +385,8 @@ class PlotRKC(PamhyrPlot):
self.canvas.figure.canvas.draw_idle() self.canvas.figure.canvas.draw_idle()
def update_bottom_with_bedload(self): def update_bottom_with_bedload(self):
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
rk = reach.geometry.get_rk() rk = reach.geometry.get_rk()
z = self.sl_compute_current_z(reach) z = self.sl_compute_current_z(reach)
+23 -9
View File
@@ -38,7 +38,7 @@ logger = logging.getLogger()
class PlotXY(PamhyrPlot): class PlotXY(PamhyrPlot):
def __init__(self, canvas=None, trad=None, toolbar=None, def __init__(self, canvas=None, trad=None, toolbar=None,
results=None, reach_id=0, profile_id=0, results=None, reach_id=0, profile_id=0, res_id=0,
display_current=True, parent=None): display_current=True, parent=None):
super(PlotXY, self).__init__( super(PlotXY, self).__init__(
canvas=canvas, canvas=canvas,
@@ -52,10 +52,11 @@ class PlotXY(PamhyrPlot):
self.line_gl = [] self.line_gl = []
self.overflow = [] self.overflow = []
self._timestamps = results.get("timestamps") self._timestamps = parent._timestamps
self._current_timestamp = max(self._timestamps) self._current_timestamp = max(self._timestamps)
self._current_reach_id = reach_id self._current_reach_id = reach_id
self._current_profile_id = profile_id self._current_profile_id = profile_id
self._current_res_id = res_id[0]
self.label_x = _translate("Results", "X (m)") self.label_x = _translate("Results", "X (m)")
self.label_y = _translate("Results", "Y (m)") self.label_y = _translate("Results", "Y (m)")
@@ -136,7 +137,7 @@ class PlotXY(PamhyrPlot):
@results.setter @results.setter
def results(self, results): def results(self, results):
self.data = results self.data = results
self._current_timestamp = max(results.get("timestamps")) self._current_timestamp = max(self._timestamps)
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
@@ -145,8 +146,12 @@ class PlotXY(PamhyrPlot):
if self.results is None: if self.results is None:
return return
reach = self.results.river.reach(self._current_reach_id) if self.results[self._current_res_id] is None:
reaches = self.results.river.reachs return
results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
reaches = results.river.reachs
self.draw_profiles(reach, reaches) self.draw_profiles(reach, reaches)
self.draw_water_elevation(reach) self.draw_water_elevation(reach)
@@ -161,6 +166,7 @@ class PlotXY(PamhyrPlot):
if reach.geometry.number_profiles == 0: if reach.geometry.number_profiles == 0:
self._init = False self._init = False
return return
self.line_xy = []
# TODO uncomment to draw all the reaches # TODO uncomment to draw all the reaches
# self.draw_other_profiles(reaches) # self.draw_other_profiles(reaches)
for xy in zip(reach.geometry.get_x(), for xy in zip(reach.geometry.get_x(),
@@ -272,7 +278,8 @@ class PlotXY(PamhyrPlot):
) )
def draw_water_elevation(self, reach): def draw_water_elevation(self, reach):
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], [] poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], []
for profile in reach.profiles: for profile in reach.profiles:
@@ -308,6 +315,10 @@ class PlotXY(PamhyrPlot):
self.update_profile() self.update_profile()
self.update_idle() self.update_idle()
def set_result(self, res_id):
self._current_res_id = res_id[0]
self.update()
def set_timestamp(self, timestamp): def set_timestamp(self, timestamp):
self._current_timestamp = timestamp self._current_timestamp = timestamp
self.update() self.update()
@@ -322,7 +333,8 @@ class PlotXY(PamhyrPlot):
self.update_idle() self.update_idle()
def update_profile(self): def update_profile(self):
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
profile = reach.profile(self._current_profile_id) profile = reach.profile(self._current_profile_id)
self.plot_selected.set_data( self.plot_selected.set_data(
@@ -331,7 +343,8 @@ class PlotXY(PamhyrPlot):
) )
def update_water_elevation(self): def update_water_elevation(self):
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], [] poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], []
for profile in reach.profiles: for profile in reach.profiles:
@@ -358,7 +371,8 @@ class PlotXY(PamhyrPlot):
self.water_fill[0].set_xy(poly) self.water_fill[0].set_xy(poly)
def update_water_elevation_overflow(self): def update_water_elevation_overflow(self):
reach = self.results.river.reach(self._current_reach_id) results = self.results[self._current_res_id]
reach = results.river.reach(self._current_reach_id)
profile = reach.profile(self._current_profile_id) profile = reach.profile(self._current_profile_id)
overflow = [] overflow = []
+25 -4
View File
@@ -23,6 +23,8 @@ from numpy import sqrt
from tools import timer, trace from tools import timer, trace
from itertools import compress
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QColor QKeySequence, QColor
) )
@@ -46,13 +48,19 @@ _translate = QCoreApplication.translate
class TableModel(PamhyrTableModel): class TableModel(PamhyrTableModel):
def _setup_lst(self): def _setup_lst(self):
_river = self._data.river _river = self._data[0].river
if self._opt_data == "reach": if self._opt_data == "reach":
self._lst = _river.reachs self._lst = _river.reachs
elif self._opt_data == "profile": elif self._opt_data == "profile":
self._lst = _river.reach(0).profiles self._lst = _river.reach(0).profiles
# self._lst = list(filter(lambda x: x.name[0:8] != 'interpol',
# _river.reach(0).profiles))
elif self._opt_data == "raw_data": elif self._opt_data == "raw_data":
self._lst = _river.reach(0).profiles self._lst = _river.reach(0).profiles
# self._lst = list(filter(lambda x: x.name[0:8] != 'interpol',
# _river.reach(0).profiles))
elif self._opt_data == "solver":
self._lst = self._parent._solvers
def __init__(self, **kwargs): def __init__(self, **kwargs):
self._timestamp = 0.0 self._timestamp = 0.0
@@ -76,6 +84,10 @@ class TableModel(PamhyrTableModel):
elif self._headers[column] == "rk": elif self._headers[column] == "rk":
v = self._lst[row].rk v = self._lst[row].rk
return f"{v:.4f}" return f"{v:.4f}"
elif self._opt_data == "solver":
if self._headers[column] == "solver":
v = self._lst[row].name
return str(v)
elif self._opt_data == "raw_data": elif self._opt_data == "raw_data":
p = self._lst[row] p = self._lst[row]
if self._headers[column] == "name": if self._headers[column] == "name":
@@ -84,14 +96,18 @@ class TableModel(PamhyrTableModel):
return f"{p.name}" return f"{p.name}"
elif self._headers[column] == "water_elevation": elif self._headers[column] == "water_elevation":
v = self._lst[row].get_ts_key(self._timestamp, "Z") v = self._lst[row].get_ts_key(self._timestamp, "Z")
if v is None:
v = 0.0
return f"{v:.4f}" return f"{v:.4f}"
elif self._headers[column] == "discharge": elif self._headers[column] == "discharge":
v = self._lst[row].get_ts_key(self._timestamp, "Q") v = self._lst[row].get_ts_key(self._timestamp, "Q")
if v is None:
v = 0.0
return f"{v:.4f}" return f"{v:.4f}"
elif self._headers[column] == "velocity": elif self._headers[column] == "velocity":
q = self._lst[row].get_ts_key(self._timestamp, "Q")
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].get_ts_key(self._timestamp, "V") v = self._lst[row].get_ts_key(self._timestamp, "V")
if v is None:
v = 0.0
return f"{v:.4f}" return f"{v:.4f}"
elif self._headers[column] == "width": elif self._headers[column] == "width":
z = self._lst[row].get_ts_key(self._timestamp, "Z") z = self._lst[row].get_ts_key(self._timestamp, "Z")
@@ -123,6 +139,9 @@ class TableModel(PamhyrTableModel):
v = self._lst[row].get_ts_key(self._timestamp, "V") v = self._lst[row].get_ts_key(self._timestamp, "V")
a = self._lst[row].geometry.wet_area(z) a = self._lst[row].geometry.wet_area(z)
b = self._lst[row].geometry.wet_width(z) b = self._lst[row].geometry.wet_width(z)
if b == 0.0 or a == 0.0:
froude = 0.0
else:
froude = v / sqrt(9.81 * (a / b)) froude = v / sqrt(9.81 * (a / b))
return f"{froude:.4f}" return f"{froude:.4f}"
else: else:
@@ -132,12 +151,14 @@ class TableModel(PamhyrTableModel):
return QVariant() return QVariant()
def update(self, reach): def update(self, reach):
_river = self._data.river _river = self._data[0].river
if self._opt_data == "reach": if self._opt_data == "reach":
self._lst = _river.reachs self._lst = _river.reachs
elif self._opt_data == "profile" or self._opt_data == "raw_data": elif self._opt_data == "profile" or self._opt_data == "raw_data":
self._lst = _river.reach(reach).profiles self._lst = _river.reach(reach).profiles
elif self._opt_data == "solver":
self._lst = self._parent._solvers
self.layoutChanged.emit() self.layoutChanged.emit()
+5 -3
View File
@@ -45,6 +45,7 @@ _translate = QCoreApplication.translate
class TableModel(PamhyrTableModel): class TableModel(PamhyrTableModel):
def _setup_lst(self): def _setup_lst(self):
_river = self._data.river _river = self._data.river
self._timestamp = min(list(self._data.get("timestamps")))
if self._opt_data == "reach": if self._opt_data == "reach":
self._lst = _river.reachs self._lst = _river.reachs
elif self._opt_data == "profile": elif self._opt_data == "profile":
@@ -57,7 +58,6 @@ class TableModel(PamhyrTableModel):
self._lst = tmp_list self._lst = tmp_list
def __init__(self, type_pol, **kwargs): def __init__(self, type_pol, **kwargs):
self._timestamp = 0.0
self._type_pol = type_pol self._type_pol = type_pol
super(TableModel, self).__init__(**kwargs) super(TableModel, self).__init__(**kwargs)
@@ -99,8 +99,8 @@ class TableModel(PamhyrTableModel):
v = self._lst[row].get_ts_key( v = self._lst[row].get_ts_key(
self._timestamp, "pols")[pol_index][0] self._timestamp, "pols")[pol_index][0]
return f"{v:.4f}" return f"{v:.4f}"
if (self._headers[column] == pol + " Mass" if self._headers[column] == pol + " Mass":
and self._type_pol[pol_index] == 7): if self._type_pol[pol_index] == 7:
m1 = self._lst[row].get_ts_key( m1 = self._lst[row].get_ts_key(
self._timestamp, "pols")[pol_index][1] self._timestamp, "pols")[pol_index][1]
m2 = self._lst[row].get_ts_key( m2 = self._lst[row].get_ts_key(
@@ -109,6 +109,8 @@ class TableModel(PamhyrTableModel):
self._timestamp, "pols")[pol_index][3] self._timestamp, "pols")[pol_index][3]
v = m1 + m2 + m3 v = m1 + m2 + m3
return f"{v:.4f}" return f"{v:.4f}"
else:
return ""
return QVariant() return QVariant()
+431 -71
View File
@@ -19,6 +19,7 @@
import os import os
import csv import csv
import logging import logging
# import rasterio
from numpy import sqrt from numpy import sqrt
@@ -35,6 +36,7 @@ from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot, QCoreApplication, QModelIndex, pyqtSlot,
QItemSelectionModel, QTimer, QItemSelectionModel, QTimer,
QSettings
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
@@ -59,10 +61,14 @@ from View.Results.CustomPlot.Plot import CustomPlot
from View.Results.CustomPlot.CustomPlotValuesSelectionDialog import ( from View.Results.CustomPlot.CustomPlotValuesSelectionDialog import (
CustomPlotValuesSelectionDialog, CustomPlotValuesSelectionDialog,
) )
from View.Results.CustomExport.CustomExport import CustomExportDialog
from View.Results.Table import TableModel from View.Results.Table import TableModel
from View.Results.translate import ResultsTranslate from View.Results.translate import (
from View.Stricklers.Window import StricklersWindow ResultsTranslate,
CompareResultsTranslate
)
from View.Results.CoordinatesDialog import CoordinatesDialog
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
@@ -82,16 +88,17 @@ class ResultsWindow(PamhyrWindow):
) )
def __init__(self, study=None, config=None, def __init__(self, study=None, config=None,
solver=None, results=None, results=None, parent=None, trad=None):
parent=None): self._solvers = [r._solver for r in results]
self._solver = solver
self._results = results self._results = results
self._current_results = [0]
if trad is None:
trad = ResultsTranslate() trad = ResultsTranslate()
name = ( name = (
trad[self._pamhyr_name] + " - " trad[self._pamhyr_name] + " - "
+ study.name + " - " + study.name + " - "
+ self._solver.name + " - ".join([s.name for s in self._solvers])
) )
super(ResultsWindow, self).__init__( super(ResultsWindow, self).__init__(
@@ -102,14 +109,15 @@ class ResultsWindow(PamhyrWindow):
parent=parent parent=parent
) )
self._hash_data.append(self._solver) for r in results:
self._hash_data.append(self._results) self._hash_data.append(r._solver)
self._hash_data.append(r._repertory)
self._hash_data.append(r._name)
self._additional_plot = {} self._additional_plot = {}
try: try:
self._timestamps = sorted(list(self._results.get("timestamps"))) self.get_timestamps()
self.setup_slider() self.setup_slider()
self.setup_table() self.setup_table()
self.setup_plots() self.setup_plots()
@@ -119,22 +127,31 @@ class ResultsWindow(PamhyrWindow):
logger_exception(e) logger_exception(e)
return return
self.update(reach_id=0,
profile_id=[0])
self.update_table_selection_solver(0)
def setup_table(self): def setup_table(self):
self._table = {} self._table = {}
for t in ["reach", "profile", "raw_data"]: for t in ["reach", "profile", "raw_data", "solver"]:
table = self.find(QTableView, f"tableView_{t}") table = self.find(QTableView, f"tableView_{t}")
self._table[t] = TableModel( self._table[t] = TableModel(
table_view=table, table_view=table,
table_headers=self._trad.get_dict(f"table_headers_{t}"), table_headers=self._trad.get_dict(f"table_headers_{t}"),
data=self._results, data=self._results,
undo=self._undo_stack, undo=self._undo_stack,
opt_data=t opt_data=t,
parent=self
) )
self._table[t]._timestamp = self._timestamps[ self._table[t]._timestamp = self._timestamps[
self._slider_time.value()] self._slider_time.value()]
if len(self._results) <= 1:
table = self.find(QTableView, f"tableView_solver")
table.hide()
def setup_slider(self): def setup_slider(self):
default_reach = self._results.river.reach(0) default_reach = self._results[self._current_results[0]].river.reach(0)
self._slider_time = self.find(QSlider, f"horizontalSlider_time") self._slider_time = self.find(QSlider, f"horizontalSlider_time")
self._slider_time.setMaximum(len(self._timestamps) - 1) self._slider_time.setMaximum(len(self._timestamps) - 1)
@@ -181,6 +198,7 @@ class ResultsWindow(PamhyrWindow):
results=self._results, results=self._results,
reach_id=0, reach_id=0,
profile_id=0, profile_id=0,
res_id=self._current_results,
trad=self._trad, trad=self._trad,
toolbar=self.toolbar, toolbar=self.toolbar,
display_current=True, display_current=True,
@@ -205,8 +223,10 @@ class ResultsWindow(PamhyrWindow):
results=self._results, results=self._results,
reach_id=0, reach_id=0,
profile_id=0, profile_id=0,
res_id=self._current_results,
trad=self._trad, trad=self._trad,
toolbar=self.toolbar_2 toolbar=self.toolbar_2,
parent=self
) )
self.plot_rkc.draw() self.plot_rkc.draw()
@@ -227,8 +247,10 @@ class ResultsWindow(PamhyrWindow):
results=self._results, results=self._results,
reach_id=0, reach_id=0,
profile_id=0, profile_id=0,
res_id=self._current_results,
trad=self._trad, trad=self._trad,
toolbar=self.toolbar_3 toolbar=self.toolbar_3,
parent=self
) )
self.plot_ac.draw() self.plot_ac.draw()
@@ -249,9 +271,11 @@ class ResultsWindow(PamhyrWindow):
canvas=self.canvas_4, canvas=self.canvas_4,
results=self._results, results=self._results,
reach_id=0, reach_id=0,
profile_id=0, profile_id=[0],
res_id=self._current_results,
trad=self._trad, trad=self._trad,
toolbar=self.toolbar_4 toolbar=self.toolbar_4,
parent=self
) )
self.plot_h.draw() self.plot_h.draw()
@@ -264,16 +288,12 @@ class ResultsWindow(PamhyrWindow):
super(ResultsWindow, self).closeEvent(event) super(ResultsWindow, self).closeEvent(event)
def _compute_status_label(self): def _compute_status_label(self):
# Timestamp
ts = self._timestamps[self._slider_time.value()]
t0 = datetime.fromtimestamp(0) return (self.text_bief() + " | " +
fts = str( self.text_profile() + " | " +
datetime.fromtimestamp(ts) - t0 self.text_time())
)
fts.replace("days", _translate("Results", "days"))\
.replace("day", _translate("Results", "day"))
def text_bief(self):
# Reach # Reach
table = self.find(QTableView, f"tableView_reach") table = self.find(QTableView, f"tableView_reach")
indexes = table.selectedIndexes() indexes = table.selectedIndexes()
@@ -281,7 +301,16 @@ class ResultsWindow(PamhyrWindow):
reach = self._study.river.enable_edges()[0] reach = self._study.river.enable_edges()[0]
else: else:
reach = self._study.river.enable_edges()[indexes[0].row()] reach = self._study.river.enable_edges()[indexes[0].row()]
return f"{self._trad['reach']}: {reach.name}"
def text_profile(self):
# Reach
table = self.find(QTableView, f"tableView_reach")
indexes = table.selectedIndexes()
if len(indexes) == 0:
reach = self._study.river.enable_edges()[0]
else:
reach = self._study.river.enable_edges()[indexes[0].row()]
# Profile # Profile
table = self.find(QTableView, f"tableView_profile") table = self.find(QTableView, f"tableView_profile")
indexes = table.selectedIndexes() indexes = table.selectedIndexes()
@@ -291,10 +320,22 @@ class ResultsWindow(PamhyrWindow):
profile = reach.reach.profile(indexes[0].row()) profile = reach.reach.profile(indexes[0].row())
pname = profile.name if profile.name != "" else profile.rk pname = profile.name if profile.name != "" else profile.rk
return f"{self._trad['cross_section']}: {pname}"
return (f"{self._trad['reach']}: {reach.name} | " + def text_time(self):
f"{self._trad['cross_section']}: {pname} | " + # Timestamp
f"{self._trad['unit_time_s']} : {fts} ({ts} sec)") ts = self._timestamps[self._slider_time.value()]
t0 = datetime.fromtimestamp(0)
fts = str(
datetime.fromtimestamp(ts) - t0
)
fts = str(
datetime.fromtimestamp(ts) - t0
)
fts.replace("days", _translate("Results", "days"))\
.replace("day", _translate("Results", "day"))
return f"{self._trad['time']} : {fts} ({ts} sec)"
def setup_statusbar(self): def setup_statusbar(self):
txt = self._compute_status_label() txt = self._compute_status_label()
@@ -312,7 +353,13 @@ class ResultsWindow(PamhyrWindow):
"action_add": self._add_custom_plot, "action_add": self._add_custom_plot,
"action_export": self._export, "action_export": self._export,
# "action_export": self.export_current, # "action_export": self.export_current,
"action_Geo_tiff": self.import_geotiff
} }
self.find(QAction, "action_Geo_tiff").setEnabled(False)
self.find(QAction, "action_Geo_tiff").setVisible(False)
if len(self._results) > 1:
self.find(QAction, "action_reload").setEnabled(False)
for action in actions: for action in actions:
self.find(QAction, action).triggered.connect( self.find(QAction, action).triggered.connect(
@@ -324,9 +371,10 @@ class ResultsWindow(PamhyrWindow):
"reach": self._set_current_reach, "reach": self._set_current_reach,
"profile": self._set_current_profile, "profile": self._set_current_profile,
"raw_data": self._set_current_profile_raw_data, "raw_data": self._set_current_profile_raw_data,
"solver": self._set_current_results,
} }
for t in ["reach", "profile", "raw_data"]: for t in ["reach", "profile", "raw_data", "solver"]:
table = self.find(QTableView, f"tableView_{t}") table = self.find(QTableView, f"tableView_{t}")
table.selectionModel()\ table.selectionModel()\
@@ -344,6 +392,10 @@ class ResultsWindow(PamhyrWindow):
self._button_last.clicked.connect(self._last) self._button_last.clicked.connect(self._last)
self._timer.timeout.connect(self._update_slider) self._timer.timeout.connect(self._update_slider)
# tabs
tab_widget = self.find(QTabWidget, f"tabWidget")
tab_widget.currentChanged.connect(self.tab_changed)
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")
selectionModel = table.selectionModel() selectionModel = table.selectionModel()
@@ -374,7 +426,25 @@ class ResultsWindow(PamhyrWindow):
) )
table.scrollTo(index) table.scrollTo(index)
def update(self, reach_id=None, profile_id=None, timestamp=None): def update_table_selection_solver(self, ind):
table = self.find(QTableView, f"tableView_solver")
selectionModel = table.selectionModel()
index = table.model().index(ind, 0)
selectionModel.select(
index,
QItemSelectionModel.Rows |
QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Select
)
table.scrollTo(index)
def update(self,
reach_id=None,
profile_id=None,
solver_id=None,
timestamp=None):
if reach_id is not None: if reach_id is not None:
self.plot_xy.set_reach(reach_id) self.plot_xy.set_reach(reach_id)
self.plot_ac.set_reach(reach_id) self.plot_ac.set_reach(reach_id)
@@ -388,15 +458,27 @@ class ResultsWindow(PamhyrWindow):
self.update_table_selection_profile(0) self.update_table_selection_profile(0)
if profile_id is not None: if profile_id is not None:
self.plot_xy.set_profile(profile_id) self.plot_xy.set_profile(profile_id[0])
self.plot_ac.set_profile(profile_id) self.plot_ac.set_profile(profile_id[0])
self.plot_rkc.set_profile(profile_id) self.plot_rkc.set_profile(profile_id[0])
self.plot_h.set_profile(profile_id) self.plot_h.set_profile(profile_id)
for plot in self._additional_plot: for plot in self._additional_plot:
self._additional_plot[plot].set_profile(profile_id) self._additional_plot[plot].set_profile(profile_id[0])
self.update_table_selection_profile(profile_id) tab_widget = self.find(QTabWidget, f"tabWidget")
if tab_widget.currentIndex() != 2:
self.update_table_selection_profile(profile_id[0])
if solver_id is not None:
self._current_results = solver_id
self.plot_xy.set_result(solver_id)
self.plot_ac.set_result(solver_id)
self.plot_rkc.set_result(solver_id)
self.plot_h.set_result(solver_id)
for plot in self._additional_plot:
self._additional_plot[plot].set_result(solver_id)
if timestamp is not None: if timestamp is not None:
self.plot_xy.set_timestamp(timestamp) self.plot_xy.set_timestamp(timestamp)
@@ -427,6 +509,14 @@ class ResultsWindow(PamhyrWindow):
return indexes[0].row() return indexes[0].row()
def _get_current_profiles_list(self):
table = self.find(QTableView, f"tableView_profile")
indexes = table.selectedIndexes()
if len(indexes) == 0:
return []
return [i.row() for i in indexes]
def _get_current_timestamp(self): def _get_current_timestamp(self):
return self._timestamps[ return self._timestamps[
self._slider_time.value() self._slider_time.value()
@@ -442,21 +532,22 @@ class ResultsWindow(PamhyrWindow):
def _set_current_profile(self): def _set_current_profile(self):
table = self.find(QTableView, f"tableView_profile") table = self.find(QTableView, f"tableView_profile")
indexes = table.selectedIndexes() indexes = table.selectionModel().selectedRows()
if len(indexes) == 0: if len(indexes) == 0:
return return
ind = indexes[0].row() self.update(profile_id=[i.row() for i in indexes])
self.update(profile_id=ind)
def _set_current_profile_raw_data(self): def _set_current_profile_raw_data(self):
table = self.find(QTableView, f"tableView_raw_data") return
def _set_current_results(self):
table = self.find(QTableView, f"tableView_solver")
indexes = table.selectedIndexes() indexes = table.selectedIndexes()
if len(indexes) == 0: if len(indexes) == 0:
return return
ind = indexes[0].row() self.update(solver_id=[i.row() for i in indexes])
self.update(profile_id=ind)
def _set_current_timestamp(self): def _set_current_timestamp(self):
timestamp = self._timestamps[self._slider_time.value()] timestamp = self._timestamps[self._slider_time.value()]
@@ -480,14 +571,17 @@ class ResultsWindow(PamhyrWindow):
def _reload(self): def _reload(self):
logger.debug("Reload results...") logger.debug("Reload results...")
self._results = self._results.reload() if len(self._results) == 1:
self._results[0] = self._results[0].reload()
self._timestamps = sorted(list(self._results.get("timestamps"))) self.get_timestamps()
self._reload_plots() self._reload_plots()
self._reload_slider() self._reload_slider()
def _add_custom_plot(self): def _add_custom_plot(self):
profile_id = self._get_current_profile()
self.update_table_selection_profile(profile_id)
dlg = CustomPlotValuesSelectionDialog(parent=self) dlg = CustomPlotValuesSelectionDialog(parent=self)
if dlg.exec(): if dlg.exec():
x, y, envelop = dlg.value x, y, envelop = dlg.value
@@ -526,6 +620,7 @@ class ResultsWindow(PamhyrWindow):
self._get_current_timestamp(), self._get_current_timestamp(),
data=self._results, data=self._results,
canvas=canvas, canvas=canvas,
res_id=self._current_results,
toolbar=toolbar, toolbar=toolbar,
parent=self, parent=self,
) )
@@ -541,6 +636,25 @@ class ResultsWindow(PamhyrWindow):
tab_widget.setCurrentWidget(widget) tab_widget.setCurrentWidget(widget)
def _copy(self): def _copy(self):
# focus on raw data table
if self.find(QTabWidget, f"tabWidget").currentIndex() == 0:
table = self.find(QTableView, f"tableView_raw_data")
model = self._table["raw_data"]
coltext = []
for index in sorted(table.selectionModel().selectedRows()):
row = index.row()
rowtext = []
for column in range(model.columnCount()):
i = model.createIndex(row, column)
try:
rowtext.append(model.data(i))
except AttributeError:
rowtext.append("")
coltext.append(rowtext)
self.copyTableIntoClipboard(coltext)
else:
logger.info("TODO: copy") logger.info("TODO: copy")
def _paste(self): def _paste(self):
@@ -588,10 +702,12 @@ class ResultsWindow(PamhyrWindow):
self._button_play.setIcon(self._icon_start) self._button_play.setIcon(self._icon_start)
def _export(self): def _export(self):
profile_id = self._get_current_profile()
self.update_table_selection_profile(profile_id)
dlg = CustomPlotValuesSelectionDialog(parent=self) dlg = CustomExportDialog(parent=self)
if dlg.exec(): if dlg.exec():
x, y, envelop = dlg.value x, y, envelop, solver_id = dlg.value
else: else:
return return
@@ -601,27 +717,28 @@ class ResultsWindow(PamhyrWindow):
) )
self.file_dialog( self.file_dialog(
select_file="AnyFile", select_file="AnyFile",
callback=lambda f: self.export_to(f[0], x, y, envelop), callback=lambda f: self.export_to(f[0], x, y, envelop, solver_id),
default_suffix=".csv", default_suffix=".csv",
file_filter=["CSV (*.csv)"], file_filter=["CSV (*.csv)"],
) )
def export_to(self, filename, x, y, envelop): def export_to(self, filename, x, y, envelop, solver_id):
timestamps = sorted(self._results.get("timestamps")) results = self._results[solver_id]
reach = self._results.river.reachs[self._get_current_reach()] reach = results.river.reachs[self._get_current_reach()]
first_line = [f"Study: {self._results.study.name}", first_line = [f"Study: {results.study.name}",
f"Reach: {reach.name}"] f"Reach: {reach.name}"]
if x == "rk": if x == "rk":
timestamp = self._get_current_timestamp() timestamp = self._get_current_timestamp()
first_line.append(f"Time: {timestamp}s") first_line.append(f"Time: {timestamp}s")
val_dict = self._export_rk(timestamp, y, envelop) val_dict = self._export_rk(timestamp, y, envelop, solver_id)
elif x == "time": elif x == "time":
profile_id = self._get_current_profile() profile_id = self._get_current_profile()
profile = reach.profile(profile_id) profile = reach.profile(profile_id)
pname = profile.name if profile.name != "" else profile.rk pname = profile.name if profile.name != "" else profile.rk
first_line.append(f"Profile: {pname}") first_line.append(f"Profile: {pname}")
val_dict = self._export_time(profile_id, y) val_dict = self._export_time(profile_id, y, solver_id)
try:
with open(filename, 'w', newline='') as csvfile: with open(filename, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',', writer = csv.writer(csvfile, delimiter=',',
quotechar='|', quoting=csv.QUOTE_MINIMAL) quotechar='|', quoting=csv.QUOTE_MINIMAL)
@@ -636,6 +753,14 @@ class ResultsWindow(PamhyrWindow):
for var in val_dict.keys(): for var in val_dict.keys():
line.append(val_dict[var][row]) line.append(val_dict[var][row])
writer.writerow(line) writer.writerow(line)
self._timer.stop()
except Exception as e:
self.message_box(
window_title=self._trad["Warning"],
text=self._trad["mb_write_error"],
informative_text=self._trad["mb_close_file"]
)
logger_exception(e)
def export_all(self, reach, directory, timestamps): def export_all(self, reach, directory, timestamps):
name = reach.name name = reach.name
@@ -648,6 +773,7 @@ class ResultsWindow(PamhyrWindow):
f"reach_{name}.csv" f"reach_{name}.csv"
) )
try:
with open(file_name, 'w', newline='') as csvfile: with open(file_name, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',', writer = csv.writer(csvfile, delimiter=',',
quotechar='|', quoting=csv.QUOTE_MINIMAL) quotechar='|', quoting=csv.QUOTE_MINIMAL)
@@ -659,6 +785,13 @@ class ResultsWindow(PamhyrWindow):
index = self._table["raw_data"].index(row, column) index = self._table["raw_data"].index(row, column)
line.append(self._table["raw_data"].data(index)) line.append(self._table["raw_data"].data(index))
writer.writerow(line) writer.writerow(line)
except Exception as e:
self.message_box(
window_title=self._trad["Warning"],
text=self._trad["mb_write_errore"],
informative_text=self._trad["mb_close_file"]
)
logger_exception(e)
def export_current(self): def export_current(self):
self.file_dialog( self.file_dialog(
@@ -667,7 +800,8 @@ class ResultsWindow(PamhyrWindow):
) )
def export_current_to(self, directory): def export_current_to(self, directory):
reach = self._results.river.reachs[self._get_current_reach()] results = self._results[self._current_results]
reach = results.river.reachs[self._get_current_reach()]
self.export_all(reach, directory, [self._get_current_timestamp()]) self.export_all(reach, directory, [self._get_current_timestamp()])
def delete_tab(self, index): def delete_tab(self, index):
@@ -675,16 +809,32 @@ class ResultsWindow(PamhyrWindow):
self._additional_plot.pop(tab_widget.tabText(index)) self._additional_plot.pop(tab_widget.tabText(index))
tab_widget.removeTab(index) tab_widget.removeTab(index)
def _export_rk(self, timestamp, y, envelop): def _export_rk(self, timestamp, y, envelop, solver_id):
reach = self._results.river.reachs[self._get_current_reach()] results = self._results[solver_id]
reach = results.river.reachs[self._get_current_reach()]
dict_x = self._trad.get_dict("values_x") dict_x = self._trad.get_dict("values_x")
dict_y = self._trad.get_dict("values_y") dict_y = self._trad.get_dict("values_y")
if self._current_results == 2:
envelop = False
reach1 = results[0].river.reachs[self._get_current_reach()]
reach2 = results[1].river.reachs[self._get_current_reach()]
if envelop: if envelop:
dict_y.update(self._trad.get_dict("values_y_envelop")) dict_y.update(self._trad.get_dict("values_y_envelop"))
my_dict = {} my_dict = {}
my_dict[dict_x["rk"]] = reach.geometry.get_rk() my_dict[dict_x["rk"]] = reach.geometry.get_rk()
if "bed_elevation" in y: if "bed_elevation" in y:
my_dict[dict_y["bed_elevation"]] = reach.geometry.get_z_min() if self._current_results != 2:
zmin = reach.geometry.get_z_min()
else:
z_min1 = reach1.geometry.get_z_min()
z_min2 = reach2.geometry.get_z_min()
zmin = list(
map(
lambda x, y: x - y,
z_min2, z_min2
)
)
my_dict[dict_y["bed_elevation"]] = zmin
# if envelop and reach.has_sediment(): # if envelop and reach.has_sediment():
if "discharge" in y: if "discharge" in y:
my_dict[dict_y["discharge"]] = list( my_dict[dict_y["discharge"]] = list(
@@ -744,13 +894,36 @@ class ResultsWindow(PamhyrWindow):
my_dict[dict_y["max_velocity"]] = [max(v) for v in velocities] my_dict[dict_y["max_velocity"]] = [max(v) for v in velocities]
if "depth" in y: if "depth" in y:
my_dict[dict_y["depth"]] = list( if self._current_results != 2:
d = my_dict[dict_y["depth"]] = list(
map( map(
lambda p: p.geometry.max_water_depth( lambda p: p.geometry.max_water_depth(
p.get_ts_key(timestamp, "Z")), p.get_ts_key(timestamp, "Z")),
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
my_dict[dict_y["depth"]] = d
if envelop: if envelop:
my_dict[dict_y["min_depth"]] = list(map( my_dict[dict_y["min_depth"]] = list(map(
lambda p1, p2: p1 - p2, map( lambda p1, p2: p1 - p2, map(
@@ -768,15 +941,39 @@ class ResultsWindow(PamhyrWindow):
) )
if "mean_depth" in y: if "mean_depth" in y:
my_dict[dict_y["mean_depth"]] = list( if self._current_results != 2:
d = list(
map( map(
lambda p: p.geometry.mean_water_depth( lambda p: p.geometry.mean_water_depth(
p.get_ts_key(timestamp, "Z")), p.get_ts_key(timestamp, "Z")),
reach.profiles reach.profiles
) )
) )
else:
d1 = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(timestamp, "Z")),
reach1.profiles
)
)
d2 = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(timestamp, "Z")),
reach2.profiles
)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
my_dict[dict_y["mean_depth"]] = d
if "froude" in y: if "froude" in y:
my_dict[dict_y["froude"]] = list( if self._current_results != 2:
fr = my_dict[dict_y["froude"]] = list(
map( map(
lambda p: lambda p:
p.get_ts_key(timestamp, "V") / p.get_ts_key(timestamp, "V") /
@@ -789,32 +986,112 @@ class ResultsWindow(PamhyrWindow):
reach.profiles reach.profiles
) )
) )
else:
fr1 = list(
map(
lambda p:
p.get_ts_key(timestamp, "V") /
sqrt(9.81 * (
p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width(
p.get_ts_key(timestamp, "Z"))
)),
reach1.profiles
)
)
fr2 = list(
map(
lambda p:
p.get_ts_key(timestamp, "V") /
sqrt(9.81 * (
p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width(
p.get_ts_key(timestamp, "Z"))
)),
reach2.profiles
)
)
fr = list(
map(
lambda x, y: x - y,
fr1, fr2
)
)
my_dict[dict_y["froude"]] = fr
if "wet_area" in y: if "wet_area" in y:
my_dict[dict_y["wet_area"]] = list( if self._current_results != 2:
wa = list(
map( map(
lambda p: p.geometry.wet_area( lambda p: p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")), p.get_ts_key(timestamp, "Z")),
reach.profiles reach.profiles
) )
) )
else:
wa1 = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")),
reach1.profiles
)
)
wa2 = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")),
reach2.profiles
)
)
wa = list(
map(
lambda x, y: x - y,
wa1, wa2
)
)
my_dict[dict_y["wet_area"]] = wa
return my_dict return my_dict
def _export_time(self, profile, y): def _export_time(self, profile, y, solver_id):
reach = self._results.river.reachs[self._get_current_reach()] results = self._results[solver_id]
reach = results.river.reachs[self._get_current_reach()]
profile = reach.profile(profile) profile = reach.profile(profile)
ts = list(self._results.get("timestamps"))
ts.sort()
dict_x = self._trad.get_dict("values_x") dict_x = self._trad.get_dict("values_x")
dict_y = self._trad.get_dict("values_y") dict_y = self._trad.get_dict("values_y")
my_dict = {} my_dict = {}
my_dict[dict_x["time"]] = ts my_dict[dict_x["time"]] = self._timestamps
z = profile.get_key("Z") z = profile.get_key("Z")
q = profile.get_key("Q") q = profile.get_key("Q")
v = profile.get_key("V") v = profile.get_key("V")
if self._current_results == 2:
reach1 = self._results[0].river.reach(self._reach)
reach2 = self._results[1].river.reach(self._reach)
profile1 = reach1.profile(self._profile)
profile2 = reach2.profile(self._profile)
q1 = profile1.get_key("Q")
z1 = profile1.get_key("Z")
v1 = profile1.get_key("V")
q2 = profile2.get_key("Q")
z2 = profile2.get_key("Z")
v2 = profile2.get_key("V")
if "bed_elevation" in y: if "bed_elevation" in y:
my_dict[dict_y["bed_elevation"]] = [ if self._current_results != 2:
profile.geometry.z_min()] * len(ts) z_min = [profile.geometry.z_min()] * len(self._timestamps)
else:
z_min1 = profile1.geometry.z_min()
z_min2 = profile2.geometry.z_min()
z_min = list(
map(
lambda ts: z_min1 - z_min2,
self._timestamps
)
)
my_dict[dict_y["bed_elevation"]] = z_min
if "discharge" in y: if "discharge" in y:
my_dict[dict_y["discharge"]] = q my_dict[dict_y["discharge"]] = q
if "water_elevation" in y: if "water_elevation" in y:
@@ -822,24 +1099,107 @@ class ResultsWindow(PamhyrWindow):
if "velocity" in y: if "velocity" in y:
my_dict[dict_y["velocity"]] = v my_dict[dict_y["velocity"]] = v
if "depth" in y: if "depth" in y:
my_dict[dict_y["depth"]] = list( if self._current_results != 2:
d = list(
map(lambda z: profile.geometry.max_water_depth(z), z) map(lambda z: profile.geometry.max_water_depth(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.max_water_depth(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.max_water_depth(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
my_dict[dict_y["depth"]] = d
if "mean_depth" in y: if "mean_depth" in y:
my_dict[dict_y["mean_depth"]] = list( if self._current_results != 2:
d = list(
map(lambda z: profile.geometry.mean_water_depth(z), z) map(lambda z: profile.geometry.mean_water_depth(z), z)
) )
else:
d1 = list(
map(lambda z: profile1.geometry.mean_water_depth(z), z1)
)
d2 = list(
map(lambda z: profile2.geometry.mean_water_depth(z), z2)
)
d = list(
map(
lambda x, y: x - y,
d1, d2
)
)
my_dict[dict_y["mean_depth"]] = d
if "froude" in y: if "froude" in y:
my_dict[dict_y["froude"]] = list( if self._current_results != 2:
fr = my_dict[dict_y["froude"]] = list(
map(lambda z, v: map(lambda z, v:
v / sqrt(9.81 * ( v / sqrt(9.81 * (
profile.geometry.wet_area(z) / profile.geometry.wet_area(z) /
profile.geometry.wet_width(z)) profile.geometry.wet_width(z))
), z, v) ), z, v)
) )
else:
fr1 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
profile1.geometry.wet_area(z) /
profile1.geometry.wet_width(z))
), z1, v1)
)
fr2 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
profile2.geometry.wet_area(z) /
profile2.geometry.wet_width(z))
), z2, v2)
)
fr = list(
map(
lambda x, y: x - y,
fr1, fr2
)
)
my_dict[dict_y["froude"]] = fr
if "wet_area" in y: if "wet_area" in y:
my_dict[dict_y["wet_area"]] = list( if self._current_results != 2:
wa = list(
map(lambda z: profile.geometry.wet_area(z), z) map(lambda z: profile.geometry.wet_area(z), z)
) )
else:
wa1 = list(
map(lambda z: profile1.geometry.wet_area(z), z1)
)
wa2 = list(
map(lambda z: profile2.geometry.wet_area(z), z2)
)
wa = list(
map(
lambda x, y: x - y,
wa1, wa2
)
)
my_dict[dict_y["wet_area"]] = wa
return my_dict return my_dict
def get_timestamps(self):
self._timestamps = sorted(list(self._results[0].get("timestamps")))
def tab_changed(self, i):
if i != 2:
if len(self._get_current_profiles_list()) > 1:
# unselect all profiles but the first one
profile_id = self._get_current_profile()
self.update_table_selection_profile(profile_id)
def import_geotiff(self):
return
+56 -24
View File
@@ -49,7 +49,7 @@ from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Results.PlotSedAdis import PlotAdis_dx, PlotAdis_dt from View.Results.PlotSedAdis import PlotAdis_dx, PlotAdis_dt
from View.Results.CustomPlot.Plot import CustomPlot from View.Results.CustomPlot.Plot import CustomPlot
from View.Results.CustomExportAdis import ( from View.Results.CustomExport.CustomExportAdis import (
CustomExportAdisDialog, CustomExportAdisDialog,
) )
@@ -98,7 +98,8 @@ class ResultsWindowAdisTS(PamhyrWindow):
) )
self._hash_data.append(self._solver) self._hash_data.append(self._solver)
self._hash_data.append(self._results) self._hash_data.append(self._results._repertory)
self._hash_data.append(self._results._name)
self._additional_plot = {} self._additional_plot = {}
self._current_pol_id = [1] self._current_pol_id = [1]
@@ -344,24 +345,29 @@ class ResultsWindowAdisTS(PamhyrWindow):
super(ResultsWindowAdisTS, self).closeEvent(event) super(ResultsWindowAdisTS, self).closeEvent(event)
def _compute_status_label(self): def _compute_status_label(self):
# Timestamp return (self.text_bief() + " | " +
ts = self._timestamps[self._slider_time.value()] self.text_profile() + " | " +
self.text_pollutant() + " | " +
t0 = datetime.fromtimestamp(0) self.text_time())
fts = str(
datetime.fromtimestamp(ts) - t0
)
fts.replace("days", _translate("Results", "days"))\
.replace("day", _translate("Results", "day"))
def text_bief(self):
# Reach # Reach
table = self.find(QTableView, f"tableView_reach") table = self.find(QTableView, f"tableView_reach")
indexes = table.selectedIndexes() indexes = table.selectedIndexes()
if len(indexes) == 0: if len(indexes) == 0:
reach = self._study.river.edges()[0] reach = self._study.river.enable_edges()[0]
else: else:
reach = self._study.river.edges()[indexes[0].row()] reach = self._study.river.enable_edges()[indexes[0].row()]
return f"{self._trad['reach']}: {reach.name}"
def text_profile(self):
# Reach
table = self.find(QTableView, f"tableView_reach")
indexes = table.selectedIndexes()
if len(indexes) == 0:
reach = self._study.river.enable_edges()[0]
else:
reach = self._study.river.enable_edges()[indexes[0].row()]
# Profile # Profile
table = self.find(QTableView, f"tableView_profile") table = self.find(QTableView, f"tableView_profile")
indexes = table.selectedIndexes() indexes = table.selectedIndexes()
@@ -371,7 +377,24 @@ class ResultsWindowAdisTS(PamhyrWindow):
profile = reach.reach.profile(indexes[0].row()) profile = reach.reach.profile(indexes[0].row())
pname = profile.name if profile.name != "" else profile.rk pname = profile.name if profile.name != "" else profile.rk
return f"{self._trad['cross_section']}: {pname}"
def text_time(self):
# Timestamp
ts = self._timestamps[self._slider_time.value()]
t0 = datetime.fromtimestamp(0)
fts = str(
datetime.fromtimestamp(ts) - t0
)
fts = str(
datetime.fromtimestamp(ts) - t0
)
fts.replace("days", _translate("Results", "days"))\
.replace("day", _translate("Results", "day"))
return f"{self._trad['time']} : {fts} ({ts} sec)"
def text_pollutant(self):
# Pollutant # Pollutant
table = self.find(QTableView, f"tableView_pollutants") table = self.find(QTableView, f"tableView_pollutants")
indexes = table.selectedIndexes() indexes = table.selectedIndexes()
@@ -380,10 +403,7 @@ class ResultsWindowAdisTS(PamhyrWindow):
self._results.pollutants_list[i.row()+1] for i in indexes self._results.pollutants_list[i.row()+1] for i in indexes
] ]
return (f"Reach: {reach.name} | " + return (f"Pollutant: {', '.join(self.pollutant_label)}")
f"Profile: {pname} | " +
f"Pollutant: {', '.join(self.pollutant_label)} | " +
f"{self._trad['unit_time_s']} : {fts} ({ts} sec)")
def setup_statusbar(self): def setup_statusbar(self):
txt = self._compute_status_label() txt = self._compute_status_label()
@@ -554,15 +574,8 @@ class ResultsWindowAdisTS(PamhyrWindow):
self.update(pol_id=rows) self.update(pol_id=rows)
def _set_current_profile_raw_data(self): def _set_current_profile_raw_data(self):
table = self.find(QTableView, f"tableView_raw_data")
indexes = table.selectedIndexes()
if len(indexes) == 0:
return return
ind = indexes[0].row()
self.update(profile_id=ind)
self._slider_profile.setValue(ind)
def _set_current_timestamp(self): def _set_current_timestamp(self):
timestamp = self._timestamps[self._slider_time.value()] timestamp = self._timestamps[self._slider_time.value()]
self.update(timestamp=timestamp) self.update(timestamp=timestamp)
@@ -597,6 +610,25 @@ class ResultsWindowAdisTS(PamhyrWindow):
self._reload_slider() self._reload_slider()
def _copy(self): def _copy(self):
# focus on raw data table
if self.find(QTabWidget, f"tabWidget").currentIndex() == 0:
table = self.find(QTableView, f"tableView_raw_data")
model = self._table["raw_data"]
coltext = []
for index in sorted(table.selectionModel().selectedRows()):
row = index.row()
rowtext = []
for column in range(model.columnCount()):
i = model.createIndex(row, column)
try:
rowtext.append(str(model.data(i)))
except AttributeError:
rowtext.append("")
coltext.append(rowtext)
self.copyTableIntoClipboard(coltext)
else:
logger.info("TODO: copy") logger.info("TODO: copy")
def _paste(self): def _paste(self):
+55 -2
View File
@@ -42,7 +42,8 @@ class ResultsTranslate(MainTranslate):
self._dict['day'] = _translate("Results", "day") self._dict['day'] = _translate("Results", "day")
self._dict['days'] = _translate("Results", "days") self._dict['days'] = _translate("Results", "days")
self._dict['envelop'] = _translate("Results", "envelop") self._dict['envelop'] = _translate("Results", "Envelop")
self._dict['solver'] = _translate("Results", "Solver")
self._dict['x'] = _translate("Results", "X (m)") self._dict['x'] = _translate("Results", "X (m)")
@@ -52,6 +53,19 @@ class ResultsTranslate(MainTranslate):
"Results", "Results",
"Max water elevation" "Max water elevation"
) )
self._dict["file_all"] = _translate("Results", "All files (*)")
self._dict["file_geotiff"] = _translate(
"Results", "GeoTIFF file (*.tiff *.tif)")
self._dict["ImageCoordinates"] = _translate(
"Results", "Image coordinates"
)
self._dict["mb_write_error"] = _translate(
"Results", "An error occured when writing to file"
)
self._dict["mb_close_file"] = _translate(
"Results",
"If the file is in use, close it and try again"
)
self._sub_dict["table_headers_reach"] = { self._sub_dict["table_headers_reach"] = {
"name": _translate("Results", "Reach name"), "name": _translate("Results", "Reach name"),
@@ -62,10 +76,14 @@ class ResultsTranslate(MainTranslate):
} }
self._sub_dict["table_headers_profile"] = { self._sub_dict["table_headers_profile"] = {
"name": self._dict["name"], "name": _translate("Results", "Profile name"),
"rk": self._dict["unit_rk"], "rk": self._dict["unit_rk"],
} }
self._sub_dict["table_headers_solver"] = {
"solver": self._dict['solver'],
}
self._sub_dict["table_headers_pollutants"] = { self._sub_dict["table_headers_pollutants"] = {
"name": _translate("Results", "Pollutant name"), "name": _translate("Results", "Pollutant name"),
} }
@@ -134,3 +152,38 @@ class ResultsTranslate(MainTranslate):
"unit_M": self._dict["unit_mass"], "unit_M": self._dict["unit_mass"],
"unit_thickness": self._dict["unit_thickness"], "unit_thickness": self._dict["unit_thickness"],
} }
class CompareResultsTranslate(ResultsTranslate):
def __init__(self):
super(CompareResultsTranslate, self).__init__()
self._dict['label_water'] = u"Δ "+_translate(
"Results", "Water elevation"
)
self._dict['unit_elevation'] = u"Δ "+self._dict["unit_elevation"]
self._sub_dict["table_headers_raw_data"] = {
"name": _translate("Results", "Profile"),
"water_elevation": u"Δ "+self._dict["unit_water_elevation"],
"discharge": u"Δ "+self._dict["unit_discharge"],
"velocity": u"Δ "+self._dict["unit_velocity"],
"width": u"Δ "+self._dict["unit_width"],
"depth": u"Δ "+self._dict["unit_depth"],
"mean_depth": u"Δ "+self._dict["unit_mean_depth"],
"wet_area": u"Δ "+self._dict["unit_wet_area"],
"wet_perimeter": u"Δ "+self._dict["unit_wet_perimeter"],
"hydraulic_radius": u"Δ "+self._dict["unit_hydraulic_radius"],
"froude": u"Δ "+self._dict["unit_froude"],
}
self._sub_dict["values_y"] = {
"bed_elevation": u"Δ "+self._dict["unit_bed_elevation"],
"water_elevation": u"Δ "+self._dict["unit_water_elevation"],
"discharge": u"Δ "+self._dict["unit_discharge"],
"velocity": u"Δ "+self._dict["unit_velocity"],
"depth": u"Δ "+self._dict["unit_depth"],
"mean_depth": u"Δ "+self._dict["unit_mean_depth"],
"froude": u"Δ "+self._dict["unit_froude"],
"wet_area": u"Δ "+self._dict["unit_wet_area"],
}
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more