mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
No files matched your search
+58
-2
@@ -29,7 +29,8 @@ stages:
|
||||
#############
|
||||
|
||||
variables:
|
||||
MAGE_8_VERSION: "v8.3.5"
|
||||
MAGE_8_VERSION: "v8.3.6"
|
||||
CUSTOM_PYTHON_VERSION: "3.10.14-build2"
|
||||
|
||||
dl-mage8-doc:
|
||||
stage: downloads
|
||||
@@ -83,6 +84,21 @@ dl-mage8-windows:
|
||||
- mage8-windows/mailleurTT.exe
|
||||
- mage8-windows/libbief.dll
|
||||
|
||||
dl-python-linux:
|
||||
stage: downloads
|
||||
tags:
|
||||
- linux
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'setup.py' || $CI_COMMIT_TAG
|
||||
script:
|
||||
- curl -L -o python.tar.xz https://gitlab.irstea.fr/parouby/custom-python-build/-/releases/$CUSTOM_PYTHON_VERSION/downloads/python.tar.xz
|
||||
- mkdir -p linux-python/
|
||||
- cd linux-python
|
||||
- tar xvf ../python.tar.xz
|
||||
artifacts:
|
||||
paths:
|
||||
- linux-python
|
||||
|
||||
#############
|
||||
# CONFIGURE #
|
||||
#############
|
||||
@@ -262,6 +278,11 @@ build-linux:
|
||||
- cp -v ../mage8-linux/* pamhyr/mage8/
|
||||
# Copy Pamhyr
|
||||
- cp -r dist/pamhyr/* pamhyr/
|
||||
# Pamhyr script to force x11
|
||||
- echo '#!/usr/bin/env bash' > pamhyr/pamhyr-x11
|
||||
- echo "" >> pamhyr/pamhyr-x11
|
||||
- echo "GDK_BACKEND=x11 ./pamhyr $@" >> pamhyr/pamhyr-x11
|
||||
- chmod +x pamhyr/pamhyr-x11
|
||||
# Copy Pamhyr resources
|
||||
- mkdir -p pamhyr/_internal/View
|
||||
- mkdir -p pamhyr/_internal/View/ui
|
||||
@@ -333,7 +354,7 @@ build-windows:
|
||||
- python -m pip install -U -r ..\requirements.txt
|
||||
# Run Pyinstaller
|
||||
# - pyinstaller --noconsole -y ..\src\pamhyr.py
|
||||
- pyinstaller --hide-console hide-early -y ..\src\pamhyr.py
|
||||
- pyinstaller -i ../src/View/ui/ressources/icon.ico --hide-console hide-early -y ..\src\pamhyr.py
|
||||
# Create directory
|
||||
- mkdir pamhyr
|
||||
- dir
|
||||
@@ -403,6 +424,41 @@ windows-package-exe:
|
||||
- copy ..\packages\pamhyr.nsi pamhyr.nsi
|
||||
- .\make-windows-exe.bat
|
||||
|
||||
linux-python-with-sources:
|
||||
stage: package
|
||||
tags:
|
||||
- release
|
||||
- linux
|
||||
needs:
|
||||
- job: dl-python-linux
|
||||
artifacts: true
|
||||
- job: set-version
|
||||
artifacts: true
|
||||
- job: build-lang
|
||||
artifacts: true
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'setup.py' || $CI_COMMIT_TAG
|
||||
artifacts:
|
||||
paths:
|
||||
- linux-sources-with-python.tar.xz
|
||||
script:
|
||||
- git archive --format=tar.gz --output=src.tar.gz origin/$CI_COMMIT_BRANCH
|
||||
- cd linux-python
|
||||
- mkdir -p pamhyr
|
||||
- cd pamhyr
|
||||
- tar xvf ../../src.tar.gz
|
||||
- cp -v ../../VERSION ./
|
||||
- cp -v ../../src/lang/*.qm ./src/lang/
|
||||
- cd ..
|
||||
- cp ../packages/setup.sh ./setup.sh
|
||||
- sed -i 's|python3|$(dirname $0)/python/python|' setup.sh
|
||||
- sed -i 's|.pamhyr-env|$(dirname $0)/.pamhyr-env|' setup.sh
|
||||
- sed -i 's|./pamhyr|$(dirname $0)/pamhyr|' setup.sh
|
||||
- cd ..
|
||||
- mv linux-python pamhyr
|
||||
- tar cf linux-sources-with-python.tar.xz pamhyr
|
||||
- mv pamhyr linux-python # Keep 'pamhyr' path avalaible
|
||||
|
||||
########
|
||||
# HASH #
|
||||
########
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -646,7 +646,7 @@ class AddNodeCommand(QUndoCommand):
|
||||
self._node = node
|
||||
|
||||
def undo(self):
|
||||
self._graph.remove_node(self._node.name)
|
||||
self._graph.remove_node(self._node)
|
||||
|
||||
def redo(self):
|
||||
self._graph.insert_node(self._node)
|
||||
|
||||
@@ -11,3 +11,4 @@ pyinstaller>=5.11.0
|
||||
shapely>=2.0.1
|
||||
lxml>=4.9.3
|
||||
platformdirs>=4.2.0
|
||||
pyshp>=2.3.1
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
guix shell --manifest=manifest.scm -- python3 ./src/pamhyr.py $@
|
||||
@@ -0,0 +1,22 @@
|
||||
;; manifest.scm -- Pamhyr
|
||||
;; Copyright (C) 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/>.
|
||||
|
||||
(specifications->manifest
|
||||
(list "python" "zlib" "python-platformdirs" "python-lxml"
|
||||
"python-pyqt@5" "python-pyqt5-sip"
|
||||
"python-pyqtgraph" "python-qscintilla"
|
||||
"python-numpy" "python-shapely" "python-pyshp"
|
||||
"python-matplotlib"))
|
||||
@@ -3,6 +3,7 @@
|
||||
!define LIC_NAME "LICENSE"
|
||||
!define APP_NAME "PAMHYR"
|
||||
|
||||
Icon "pamhyr\_internal\View\ui\ressources\icon.ico"
|
||||
Name "Pamhyr2"
|
||||
OutFile "pamhyr-windows.exe"
|
||||
LicenseData "pamhyr\_internal\LICENSE"
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#! /usr/bin/env bash
|
||||
# setup.sh -- Pamhyr
|
||||
# Copyright (C) 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/>.
|
||||
|
||||
function perr() {
|
||||
>&2 echo $1
|
||||
}
|
||||
|
||||
function try() {
|
||||
echo "$1"
|
||||
res=$($1)
|
||||
if [ $? -ne 0 ];
|
||||
then
|
||||
perr " !!! Command failed with exit value $?: '$1'"
|
||||
perr " - $res"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
if [ $# -ne 0 ];
|
||||
then
|
||||
perr "usage: $0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OLD_PWD=$PWD
|
||||
|
||||
try "python3 -m venv .pamhyr-env"
|
||||
try "source .pamhyr-env/bin/activate"
|
||||
try "python3 -m pip install -r ./pamhyr/requirements.txt"
|
||||
|
||||
echo '#! /usr/bin/env bash' > run-pamhyr
|
||||
echo 'source .pamhyr-env/bin/activate' >> run-pamhyr
|
||||
echo 'if [ $XDG_SESSION_TYPE == wayland ]; then' >> run-pamhyr
|
||||
echo ' export QT_QPA_PLATFORM=wayland' >> run-pamhyr
|
||||
echo 'fi' >> run-pamhyr
|
||||
echo 'python3 ./pamhyr/src/pamhyr.py $@' >> run-pamhyr
|
||||
|
||||
try "chmod +x run-pamhyr"
|
||||
|
||||
cd $OLD_PWD
|
||||
@@ -11,3 +11,4 @@ pyinstaller>=5.11.0
|
||||
shapely>=2.0.1
|
||||
lxml>=4.9.3
|
||||
platformdirs>=4.2.0
|
||||
pyshp>=2.3.1
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
def read_requirements():
|
||||
req = []
|
||||
|
||||
with open("requirements.txt", "r") as f:
|
||||
for line in f:
|
||||
if line[0] == "#":
|
||||
continue
|
||||
|
||||
req.append(line)
|
||||
|
||||
return req
|
||||
|
||||
setup(
|
||||
name="Pamhyr2",
|
||||
version="0.0.8",
|
||||
description='River 1D modelling interface',
|
||||
maintainer='INRAE',
|
||||
maintainer_email='pierre-antoine.rouby@inrae.fr',
|
||||
url='https://gitlab.irstea.fr/theophile.terraz/pamhyr',
|
||||
packages=find_packages("."),
|
||||
package_dir={"Pamhyr2": "Pamhyr2"},
|
||||
scripts=[os.path.join("src", "pamhyr.py")],
|
||||
install_requires=read_requirements(),
|
||||
)
|
||||
@@ -92,6 +92,21 @@ class AbstractModelChecker(object):
|
||||
def is_error(self):
|
||||
return self._status == STATUS.ERROR
|
||||
|
||||
# Basic check
|
||||
def basic_check(self, study):
|
||||
if study is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "invalid_study"
|
||||
return False
|
||||
|
||||
river = study.river
|
||||
if river is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_river_found"
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# Abstract function
|
||||
|
||||
def _run(self, study):
|
||||
|
||||
+4
-4
@@ -206,11 +206,11 @@ class MageGeometryGuideLineChecker(StudyGeometryChecker):
|
||||
def __init__(self, version="mage8"):
|
||||
super(MageGeometryGuideLineChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Mage geometry guide line checker")
|
||||
self._name = _translate("Checker", "Mage geometry guideline checker")
|
||||
self._description = _translate(
|
||||
"Checker",
|
||||
"Check if exists geometry guide line is correctly defined \
|
||||
for each reaches of the study"
|
||||
"Check if exists geometry guidelines are correctly defined \
|
||||
for each reach"
|
||||
)
|
||||
self._solver = version
|
||||
self._modules = Modules.GEOMETRY
|
||||
@@ -241,7 +241,7 @@ for each reaches of the study"
|
||||
for profile in profiles:
|
||||
if not profile.has_standard_named_points():
|
||||
self._status = STATUS.WARNING
|
||||
self._summary = "no_standard_guileline_defined"
|
||||
self._summary = "no_standard_guideline_defined"
|
||||
return False
|
||||
|
||||
for gl in gls[1:]:
|
||||
|
||||
+127
-17
@@ -17,6 +17,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
import logging
|
||||
from functools import reduce
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
@@ -25,28 +27,24 @@ from Checker.Checker import AbstractModelChecker, STATUS
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class StudyNetworkReachChecker(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(StudyNetworkReachChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Study network reach checker")
|
||||
self._name = _translate("Checker", "Study reach network checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check if exists at least one reach for study"
|
||||
"Checker", "Check if exists at least one reach exists"
|
||||
)
|
||||
self._modules = Modules.NETWORK
|
||||
|
||||
def run(self, study):
|
||||
if study is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "invalid_study"
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
river = study.river
|
||||
if river is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_river_found"
|
||||
return False
|
||||
|
||||
edges = list(filter(lambda e: e.is_enable(), river.edges()))
|
||||
if len(edges) == 0:
|
||||
@@ -65,7 +63,7 @@ class StudyGeometryChecker(AbstractModelChecker):
|
||||
|
||||
self._name = _translate("Checker", "Study geometry checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check if exists geometry for each reach of study"
|
||||
"Checker", "Check if the geometry of each reach exists"
|
||||
)
|
||||
self._modules = Modules.GEOMETRY
|
||||
|
||||
@@ -77,16 +75,10 @@ class StudyGeometryChecker(AbstractModelChecker):
|
||||
summary = "ok"
|
||||
status = STATUS.OK
|
||||
|
||||
if study is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "invalid_study"
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
river = study.river
|
||||
if river is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_river_found"
|
||||
return False
|
||||
|
||||
edges = river.enable_edges()
|
||||
if len(edges) == 0:
|
||||
@@ -106,6 +98,124 @@ class StudyGeometryChecker(AbstractModelChecker):
|
||||
return ok
|
||||
|
||||
|
||||
class StudyInitialConditionsChecker(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(StudyInitialConditionsChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Study initial conditions checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check initial conditions for each node of study"
|
||||
)
|
||||
self._modules = Modules.INITIAL_CONDITION
|
||||
|
||||
def run(self, study):
|
||||
ok = True
|
||||
nerror = 0
|
||||
self._summary = "ok"
|
||||
self._status = STATUS.OK
|
||||
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
for reach in study.river.enable_edges():
|
||||
ok &= self.check_reach(study, reach.reach)
|
||||
|
||||
return ok
|
||||
|
||||
def check_reach(self, study, reach):
|
||||
ok = True
|
||||
river = study.river
|
||||
|
||||
if reach not in river.initial_conditions:
|
||||
self._summary = "missing_initial_condition_defined"
|
||||
self._status = STATUS.WARNING
|
||||
return ok
|
||||
|
||||
ic = river.initial_conditions[reach]
|
||||
len_ic = len(ic)
|
||||
len_reach = len(reach)
|
||||
|
||||
if len_ic < len_reach:
|
||||
self._summary = "initial_condition_missing_profile"
|
||||
self._status = STATUS.WARNING
|
||||
|
||||
if len_ic > len_reach:
|
||||
self._summary = "more_initial_condition_than_profile"
|
||||
self._status = STATUS.ERROR
|
||||
ok = False
|
||||
|
||||
return ok
|
||||
|
||||
|
||||
class StudyBoundaryConditionChecker(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(StudyBoundaryConditionChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Study boundary conditions checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check boundary conditions for each node of study"
|
||||
)
|
||||
self._modules = Modules.BOUNDARY_CONDITION
|
||||
|
||||
def run(self, study):
|
||||
ok = True
|
||||
nerror = 0
|
||||
self._summary = "ok"
|
||||
self._status = STATUS.OK
|
||||
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
ok = self.check_liquid(study)
|
||||
|
||||
return ok
|
||||
|
||||
def check_liquid(self, study):
|
||||
river = study.river
|
||||
|
||||
bcs = river.boundary_condition.get_tab('liquid')
|
||||
if len(bcs) == 0:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_boundary_condition_defined"
|
||||
return False
|
||||
|
||||
upstream, downstream = reduce(
|
||||
lambda acc, n: (
|
||||
acc[0] + [n] if river.is_upstream_node(n) else acc[0],
|
||||
acc[1] + [n] if river.is_downstream_node(n) else acc[1],
|
||||
),
|
||||
filter(
|
||||
lambda n: river.is_enable_node(n),
|
||||
river.nodes()
|
||||
),
|
||||
([], [])
|
||||
)
|
||||
|
||||
bcs_nodes = set(map(lambda bc: bc.node, bcs))
|
||||
|
||||
upstream_ok = self.check_liquid_all_node_has_bc(
|
||||
bcs_nodes, upstream
|
||||
)
|
||||
downstream_ok = self.check_liquid_all_node_has_bc(
|
||||
bcs_nodes, downstream
|
||||
)
|
||||
|
||||
ok = upstream_ok and downstream_ok
|
||||
|
||||
if not ok:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_boundary_condition_at_boundary_node"
|
||||
|
||||
return ok
|
||||
|
||||
def check_liquid_all_node_has_bc(self, bcs_nodes, nodes):
|
||||
return reduce(
|
||||
lambda acc, n: (acc and (n in bcs_nodes)),
|
||||
nodes,
|
||||
True
|
||||
)
|
||||
|
||||
|
||||
class DummyOK(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(DummyOK, self).__init__()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
+102
-16
@@ -293,10 +293,12 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
||||
def meshing(self, reach,
|
||||
step: float = 50,
|
||||
limites=[-1, -1],
|
||||
origin=0,
|
||||
directrices=['un', 'np'],
|
||||
lplan: bool = False,
|
||||
lm: int = 3,
|
||||
linear: bool = False):
|
||||
linear: bool = False,
|
||||
origin_value=0.0):
|
||||
if reach is None or len(reach.profiles) == 0:
|
||||
return reach
|
||||
|
||||
@@ -305,32 +307,41 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
||||
st_file = self.export_reach_to_st(reach, tmp)
|
||||
m_file = st_file.rsplit(".ST", 1)[0] + ".M"
|
||||
|
||||
os.sync()
|
||||
|
||||
proc = QProcess()
|
||||
proc.setWorkingDirectory(tmp)
|
||||
|
||||
# Mage section index start at 1
|
||||
origin += 1
|
||||
limites[0] += 1
|
||||
limites[1] += 1
|
||||
|
||||
lplan = 1 if lplan else 0
|
||||
linear = 1 if linear else 0
|
||||
|
||||
logger.info(
|
||||
f"! {self._exe_path()} {st_file} {m_file} " +
|
||||
f"! {self._exe_path()} " +
|
||||
f"{st_file} {m_file} " +
|
||||
f"mesh " +
|
||||
f"{str(step)} " +
|
||||
f"{limites[0]} {limites[1]} " +
|
||||
f"{directrices[0]} {directrices[1]} " +
|
||||
f"{lplan} {lm} {linear}"
|
||||
f"{lplan} {lm} {linear} " +
|
||||
f"{origin} "
|
||||
)
|
||||
proc.start(
|
||||
self._exe_path(),
|
||||
list(map(
|
||||
str,
|
||||
[
|
||||
st_file, m_file, step,
|
||||
limites[0], limites[1],
|
||||
directrices[0], directrices[1],
|
||||
lplan, lm, linear
|
||||
]
|
||||
))
|
||||
list(
|
||||
map(
|
||||
str,
|
||||
[
|
||||
st_file, m_file,
|
||||
"mesh", step,
|
||||
limites[0], limites[1],
|
||||
directrices[0], directrices[1],
|
||||
lplan, lm, linear, origin, origin_value
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
proc.waitForFinished()
|
||||
|
||||
@@ -347,9 +358,84 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
||||
logger.error(
|
||||
f"{logger_color_red()}{errors}{logger_color_reset()}"
|
||||
)
|
||||
else:
|
||||
self.import_m_file(reach, m_file)
|
||||
return reach
|
||||
|
||||
self.import_m_file(reach, m_file)
|
||||
return reach
|
||||
|
||||
def update_kp(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):
|
||||
if reach is None or len(reach.profiles) == 0:
|
||||
return reach
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
logger.debug(f"temp file: {tmp}")
|
||||
st_file = self.export_reach_to_st(reach, tmp)
|
||||
m_file = st_file.rsplit(".ST", 1)[0] + ".M"
|
||||
|
||||
os.sync()
|
||||
|
||||
proc = QProcess()
|
||||
proc.setWorkingDirectory(tmp)
|
||||
|
||||
# Mage section indices start at 1
|
||||
origin += 1
|
||||
limites[0] += 1
|
||||
limites[1] += 1
|
||||
|
||||
lplan = 1 if lplan else 0
|
||||
linear = 1 if linear else 0
|
||||
|
||||
logger.info(
|
||||
f"! {self._exe_path()} " +
|
||||
f"{st_file} {m_file} " +
|
||||
f"update_kp " +
|
||||
f"{str(step)} " +
|
||||
f"{limites[0]} {limites[1]} " +
|
||||
f"{directrices[0]} {directrices[1]} " +
|
||||
f"{lplan} {lm} {linear} " +
|
||||
f"{origin} "
|
||||
)
|
||||
proc.start(
|
||||
self._exe_path(),
|
||||
list(
|
||||
map(
|
||||
str,
|
||||
[
|
||||
st_file, m_file,
|
||||
"update_kp", step,
|
||||
limites[0], limites[1],
|
||||
directrices[0], directrices[1],
|
||||
lplan, lm, linear, origin, origin_value
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
proc.waitForFinished()
|
||||
|
||||
if proc.exitCode() != 0:
|
||||
logger.error(
|
||||
f"{logger_color_red()}" +
|
||||
f"MailleurTT error: {proc.exitCode()}" +
|
||||
f"{logger_color_reset()}"
|
||||
)
|
||||
outputs = proc.readAllStandardOutput()
|
||||
logger.debug(outputs)
|
||||
|
||||
errors = proc.readAllStandardError()
|
||||
logger.error(
|
||||
f"{logger_color_red()}{errors}{logger_color_reset()}"
|
||||
)
|
||||
return reach
|
||||
|
||||
self.import_m_file(reach, m_file)
|
||||
return reach
|
||||
|
||||
def export_reach_to_st(self, reach, tmp):
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
+2
-1
@@ -21,7 +21,7 @@ from PyQt5.QtCore import (
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QMessageBox,
|
||||
QApplication, QMessageBox, QLabel,
|
||||
)
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -34,6 +34,7 @@ _translate = QCoreApplication.translate
|
||||
def exception_message_box(exception):
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Critical)
|
||||
msg.findChild(QLabel, "qt_msgbox_label").setFixedWidth(384)
|
||||
|
||||
msg.setText("Exception :")
|
||||
msg.setInformativeText(f"{exception}")
|
||||
|
||||
@@ -209,6 +209,14 @@ class Friction(SQLSubModel):
|
||||
|
||||
self._status.modified()
|
||||
|
||||
def __contains__(self, kp):
|
||||
return self.contains_kp(kp)
|
||||
|
||||
def contains_kp(self, kp):
|
||||
return (
|
||||
self._begin_kp <= kp <= self._end_kp
|
||||
)
|
||||
|
||||
@property
|
||||
def begin_strickler(self):
|
||||
return self._begin_strickler
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -192,6 +192,22 @@ class PointXYZ(Point, SQLSubModel):
|
||||
def dist(self, p2):
|
||||
return PointXYZ.distance(self, p2)
|
||||
|
||||
def dist_2d(self, p2):
|
||||
return PointXYZ.distance_2d(self, p2)
|
||||
|
||||
@staticmethod
|
||||
def distance_2d(p1, p2):
|
||||
"""Euclidean distance between p1 and p2.
|
||||
|
||||
Args:
|
||||
p1: A XYZ Point
|
||||
p2: A XYZ Point
|
||||
|
||||
Returns:
|
||||
Euclidean 2D distance between the two points
|
||||
"""
|
||||
return dist((p1.x, p1.y), (p2.x, p2.y))
|
||||
|
||||
@staticmethod
|
||||
def distance(p1, p2):
|
||||
"""Euclidean distance between p1 and p2.
|
||||
@@ -201,6 +217,14 @@ class PointXYZ(Point, SQLSubModel):
|
||||
p2: A XYZ Point
|
||||
|
||||
Returns:
|
||||
Euclidean distance between the two points
|
||||
Euclidean 3D distance between the two points
|
||||
"""
|
||||
return dist((p1.x, p1.y, p1.z), (p2.x, p2.y, p2.z))
|
||||
|
||||
@staticmethod
|
||||
def areatriangle3d(p1, p2, p3):
|
||||
a = PointXYZ.distance(p1, p2)
|
||||
b = PointXYZ.distance(p2, p3)
|
||||
c = PointXYZ.distance(p3, p1)
|
||||
s = (a + b + c) / 2
|
||||
return (s*(s-a) * (s-b)*(s-c)) ** 0.5
|
||||
@@ -19,6 +19,7 @@
|
||||
import logging
|
||||
|
||||
from tools import timer
|
||||
from shapely import geometry
|
||||
|
||||
from Model.Geometry.Point import Point
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
@@ -295,6 +296,11 @@ class Profile(object):
|
||||
)
|
||||
self._status.modified()
|
||||
|
||||
@timer
|
||||
def reverse(self):
|
||||
self._points.reverse()
|
||||
self._status.modified()
|
||||
|
||||
# Sediment Layers
|
||||
|
||||
def get_sl(self):
|
||||
@@ -341,3 +347,12 @@ class Profile(object):
|
||||
# Abstract method for width approximation
|
||||
def get_water_limits(self, z):
|
||||
raise NotImplementedMethodeError(self, self.get_water_limits)
|
||||
|
||||
def wet_points(self, z):
|
||||
raise NotImplementedMethodeError(self, self.wet_point)
|
||||
|
||||
def wet_perimeter(self, z):
|
||||
raise NotImplementedMethodeError(self, self.wet_perimeter)
|
||||
|
||||
def wet_area(self, z):
|
||||
raise NotImplementedMethodeError(self, self.wet_area)
|
||||
@@ -22,6 +22,7 @@ from typing import List
|
||||
from functools import reduce
|
||||
|
||||
from tools import timer
|
||||
from shapely import geometry
|
||||
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import ClipboardFormatError
|
||||
@@ -373,99 +374,13 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
"""
|
||||
return [x for x in lst if not np.isnan(x)]
|
||||
|
||||
def _first_point_not_nan(self):
|
||||
first_point = self.points[0]
|
||||
def speed(self, q, z):
|
||||
area = self.wet_area(z)
|
||||
|
||||
for point in self.points:
|
||||
if not point.is_nan():
|
||||
first_point = point
|
||||
break
|
||||
if area == 0:
|
||||
return 0
|
||||
|
||||
return first_point
|
||||
|
||||
def _last_point_not_nan(self):
|
||||
last_point = self.points[-1]
|
||||
|
||||
for point in self.points[::-1]:
|
||||
if not point.is_nan():
|
||||
last_point = point
|
||||
break
|
||||
|
||||
return last_point
|
||||
|
||||
@timer
|
||||
def get_station(self) -> np.ndarray:
|
||||
"""Projection of the points of the profile on a plane.
|
||||
|
||||
Args:
|
||||
profile: The profile
|
||||
|
||||
Returns:
|
||||
Projection of the points of the profile on a plane.
|
||||
"""
|
||||
if self.nb_points < 3:
|
||||
return None
|
||||
else:
|
||||
first_named_point = None
|
||||
index_first_named_point = None
|
||||
last_named_point = None
|
||||
|
||||
first_point_not_nan = self._first_point_not_nan()
|
||||
last_point_not_nan = self._last_point_not_nan()
|
||||
|
||||
for index, point in enumerate(self.points):
|
||||
if point.point_is_named():
|
||||
index_first_named_point = index
|
||||
first_named_point = point
|
||||
break
|
||||
|
||||
for point in reversed(self.points):
|
||||
if point.point_is_named():
|
||||
last_named_point = point
|
||||
break
|
||||
|
||||
station = []
|
||||
constant = 0.0
|
||||
|
||||
if (first_named_point is not None and
|
||||
last_named_point is not None):
|
||||
if (first_named_point != last_named_point and
|
||||
first_named_point.x != last_named_point.x):
|
||||
vector = Vector1d(first_named_point, last_named_point)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
else:
|
||||
vector = Vector1d(first_point_not_nan, last_point_not_nan)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
|
||||
for point in self.points:
|
||||
xi = point.x - first_named_point.x
|
||||
yi = point.y - first_named_point.y
|
||||
station_i = (norm_dir_vec[0] * xi +
|
||||
norm_dir_vec[1] * yi)
|
||||
station.append(station_i)
|
||||
|
||||
constant = station[index_first_named_point]
|
||||
elif first_named_point is None:
|
||||
vector = Vector1d(first_point_not_nan, last_point_not_nan)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
|
||||
for point in self.points:
|
||||
xi = point.x - first_point_not_nan.x
|
||||
yi = point.y - first_point_not_nan.y
|
||||
station_i = (norm_dir_vec[0] * xi +
|
||||
norm_dir_vec[1] * yi)
|
||||
station.append(station_i)
|
||||
|
||||
z_min = self.z_min()
|
||||
index_profile_z_min = list(
|
||||
filter(
|
||||
lambda z: z[1] == z_min,
|
||||
enumerate(self.z())
|
||||
)
|
||||
)[0]
|
||||
constant = station[index_profile_z_min[0]]
|
||||
|
||||
return list(map(lambda s: s - constant, station))
|
||||
return q / area
|
||||
|
||||
def width_approximation(self):
|
||||
if self.has_standard_named_points():
|
||||
@@ -477,6 +392,42 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
|
||||
return abs(rg.dist(rd))
|
||||
|
||||
def wet_perimeter(self, z):
|
||||
poly = self.wet_polygon(z)
|
||||
|
||||
if poly is None:
|
||||
return 0
|
||||
|
||||
return poly.length
|
||||
|
||||
def wet_area(self, z):
|
||||
poly = self.wet_polygon(z)
|
||||
|
||||
if poly is None:
|
||||
return 0
|
||||
|
||||
return poly.area
|
||||
|
||||
def wet_polygon(self, z):
|
||||
points = self.wet_points(z)
|
||||
if len(points) < 3:
|
||||
return None
|
||||
|
||||
z = map(lambda p: p.z, points)
|
||||
station = self._get_station(points)
|
||||
|
||||
poly = geometry.Polygon(list(zip(station, z)))
|
||||
return poly
|
||||
|
||||
def wet_points(self, z):
|
||||
left, right = self.get_water_limits(z)
|
||||
|
||||
points = list(filter(lambda p: p.z < z, self._points))
|
||||
points = [left] + points + [right]
|
||||
points = sorted(points, key=lambda p: p.x)
|
||||
|
||||
return points
|
||||
|
||||
def get_water_limits(self, z):
|
||||
"""
|
||||
Determine left and right limits of water elevation.
|
||||
@@ -509,7 +460,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
[self.point(i_left).z, self.point(i_left - 1).z],
|
||||
[self.point(i_left).y, self.point(i_left - 1).y]
|
||||
)
|
||||
pt_left = PointXYZ(x, y, z)
|
||||
pt_left = PointXYZ(x, y, z, name="wl_left")
|
||||
else:
|
||||
pt_left = self.point(0)
|
||||
|
||||
@@ -525,8 +476,151 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
[self.point(i_right).z, self.point(i_right + 1).z],
|
||||
[self.point(i_right).y, self.point(i_right + 1).y]
|
||||
)
|
||||
pt_right = PointXYZ(x, y, z)
|
||||
pt_right = PointXYZ(x, y, z, name="wl_right")
|
||||
else:
|
||||
pt_right = self.point(self.number_points - 1)
|
||||
|
||||
return pt_left, pt_right
|
||||
|
||||
def get_station(self):
|
||||
"""Projection of the points of the profile on a plane.
|
||||
|
||||
Args:
|
||||
self: The profile
|
||||
|
||||
Returns:
|
||||
Projection of the points of the profile on a plane.
|
||||
"""
|
||||
if self.nb_points < 3:
|
||||
return None
|
||||
else:
|
||||
return self._get_station(self.points)
|
||||
|
||||
@timer
|
||||
def _get_station(self, points):
|
||||
first_named_point = None
|
||||
index_first_named_point = None
|
||||
last_named_point = None
|
||||
|
||||
first_point_not_nan = self._first_point_not_nan(points)
|
||||
last_point_not_nan = self._last_point_not_nan(points)
|
||||
|
||||
for index, point in enumerate(points):
|
||||
if point.point_is_named():
|
||||
index_first_named_point = index
|
||||
first_named_point = point
|
||||
break
|
||||
|
||||
for point in reversed(points):
|
||||
if point.point_is_named():
|
||||
last_named_point = point
|
||||
break
|
||||
|
||||
station = []
|
||||
constant = 0.0
|
||||
|
||||
if (first_named_point is not None
|
||||
and last_named_point is not None):
|
||||
if (first_named_point != last_named_point
|
||||
and first_named_point.x != last_named_point.x):
|
||||
vector = Vector1d(first_named_point, last_named_point)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
else:
|
||||
vector = Vector1d(first_point_not_nan, last_point_not_nan)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
|
||||
for point in points:
|
||||
xi = point.x - first_named_point.x
|
||||
yi = point.y - first_named_point.y
|
||||
station_i = (norm_dir_vec[0] * xi +
|
||||
norm_dir_vec[1] * yi)
|
||||
station.append(station_i)
|
||||
|
||||
constant = station[index_first_named_point]
|
||||
elif first_named_point is None:
|
||||
vector = Vector1d(first_point_not_nan, last_point_not_nan)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
|
||||
for point in points:
|
||||
xi = point.x - first_point_not_nan.x
|
||||
yi = point.y - first_point_not_nan.y
|
||||
station_i = (norm_dir_vec[0] * xi +
|
||||
norm_dir_vec[1] * yi)
|
||||
station.append(station_i)
|
||||
|
||||
z_min = self.z_min()
|
||||
index_profile_z_min = next(
|
||||
filter(
|
||||
lambda z: z[1] == z_min,
|
||||
enumerate(self.z())
|
||||
)
|
||||
)
|
||||
constant = station[index_profile_z_min[0]]
|
||||
|
||||
return list(map(lambda s: s - constant, station))
|
||||
|
||||
def _first_point_not_nan(self, points):
|
||||
first_point = None
|
||||
|
||||
for point in points:
|
||||
if not point.is_nan():
|
||||
first_point = point
|
||||
break
|
||||
|
||||
return first_point
|
||||
|
||||
def _last_point_not_nan(self, points):
|
||||
last_point = None
|
||||
|
||||
for point in reversed(points):
|
||||
if not point.is_nan():
|
||||
last_point = point
|
||||
break
|
||||
|
||||
return last_point
|
||||
|
||||
def purge(self, np_purge):
|
||||
"""
|
||||
Remove points to keep at most np_purge points.
|
||||
"""
|
||||
if (self.nb_points <= np_purge):
|
||||
return
|
||||
|
||||
nb_named = 2 # we consider the first and last point as named
|
||||
area = [0.0]
|
||||
|
||||
for i in range(1, self.nb_points-1):
|
||||
if self.point(i).point_is_named():
|
||||
area.append(9999999.999)
|
||||
nb_named += 1
|
||||
else:
|
||||
area.append(
|
||||
PointXYZ.areatriangle3d(
|
||||
self.point(i-1),
|
||||
self.point(i),
|
||||
self.point(i+1))
|
||||
)
|
||||
|
||||
area.append(0.0)
|
||||
|
||||
while self.nb_points > max(np_purge, nb_named):
|
||||
to_rm = np.argmin(area[1:self.nb_points - 1]) + 1
|
||||
|
||||
self.delete_i([to_rm])
|
||||
area.pop(to_rm)
|
||||
|
||||
for i in [to_rm-1, to_rm]:
|
||||
if (i == 0):
|
||||
continue
|
||||
|
||||
if (i == self.nb_points - 1):
|
||||
continue
|
||||
|
||||
if self.point(i).point_is_named():
|
||||
area[i] = 9999999.999
|
||||
else:
|
||||
area[i] = PointXYZ.areatriangle3d(
|
||||
self.point(i-1),
|
||||
self.point(i),
|
||||
self.point(i+1)
|
||||
)
|
||||
+106
-1
@@ -16,7 +16,9 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import logging
|
||||
import shapefile
|
||||
import numpy as np
|
||||
|
||||
from time import time
|
||||
@@ -364,6 +366,30 @@ class Reach(SQLSubModel):
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
def inter_profiles_kp(self):
|
||||
profiles = sorted(self.profiles, key=lambda p: p.kp)
|
||||
first = profiles[0]
|
||||
last = profiles[-1]
|
||||
|
||||
res_kp = [first.kp]
|
||||
|
||||
profiles = iter(profiles)
|
||||
previous = next(profiles)
|
||||
|
||||
for profile in profiles:
|
||||
prev = previous.kp
|
||||
curr = profile.kp
|
||||
|
||||
diff = abs(previous.kp - profile.kp)
|
||||
|
||||
new = previous.kp + (diff / 2.0)
|
||||
res_kp.append(new)
|
||||
|
||||
previous = profile
|
||||
|
||||
res_kp.append(last.kp)
|
||||
return res_kp
|
||||
|
||||
# Sediment Layers
|
||||
|
||||
def get_sl(self):
|
||||
@@ -533,8 +559,66 @@ class Reach(SQLSubModel):
|
||||
|
||||
# Import/Export
|
||||
|
||||
@timer
|
||||
def import_geometry(self, file_path_name: str):
|
||||
ext = file_path_name.split(".")
|
||||
if len(ext) > 0:
|
||||
ext = ext[-1].lower()
|
||||
|
||||
if ext in ['st', 'm']:
|
||||
return self.import_geometry_st(file_path_name)
|
||||
|
||||
if ext in ['shp']:
|
||||
return self.import_geometry_shp(file_path_name)
|
||||
|
||||
raise FileFormatError(
|
||||
file_path_name,
|
||||
f"Geometry file extention ('.{ext}') unkown"
|
||||
)
|
||||
|
||||
@timer
|
||||
def import_geometry_shp(self, file_path_name: str):
|
||||
sf = shapefile.Reader(
|
||||
os.path.abspath(file_path_name)
|
||||
)
|
||||
|
||||
if sf.shapeType != shapefile.POLYLINEZ:
|
||||
raise FileFormatError(
|
||||
file_path_name,
|
||||
"Shapefile is not a POLYLINEZ"
|
||||
)
|
||||
|
||||
profiles = []
|
||||
|
||||
for i in range(len(sf)):
|
||||
s_profile = sf.shape(i)
|
||||
z = s_profile.z
|
||||
|
||||
ind = 0
|
||||
points = []
|
||||
for point in s_profile.points:
|
||||
points.append([
|
||||
point[0],
|
||||
point[1],
|
||||
z[ind]
|
||||
])
|
||||
|
||||
ind += 1
|
||||
|
||||
prof = ProfileXYZ(
|
||||
reach=self, status=self._status
|
||||
)
|
||||
prof.import_points(points)
|
||||
|
||||
profiles.append(prof)
|
||||
|
||||
self.profiles = profiles + self.profiles
|
||||
self._update_profile_numbers()
|
||||
self._recompute_kp()
|
||||
|
||||
return profiles
|
||||
|
||||
@timer
|
||||
def import_geometry_st(self, file_path_name: str):
|
||||
"""Import a geometry from file (.ST or .st)
|
||||
|
||||
Args:
|
||||
@@ -762,3 +846,24 @@ class Reach(SQLSubModel):
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return 0
|
||||
|
||||
def _recompute_kp(self, offset=0.0):
|
||||
self._recompute_kp_no_gl(offset=offset)
|
||||
|
||||
def _recompute_kp_no_gl(self, offset=0.0):
|
||||
profiles = iter(self.profiles)
|
||||
|
||||
previous = next(profiles)
|
||||
previous.kp = offset
|
||||
|
||||
for profile in profiles:
|
||||
prev_points = previous.points
|
||||
curr_points = profile.points
|
||||
|
||||
dist = (
|
||||
prev_points[0].dist_2d(curr_points[0]) +
|
||||
prev_points[-1].dist_2d(curr_points[-1])
|
||||
) / 2.0
|
||||
|
||||
profile.kp = previous.kp + dist
|
||||
previous = profile
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -266,6 +266,9 @@ class InitialConditions(SQLSubModel):
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
def lst(self):
|
||||
return self._data
|
||||
|
||||
@property
|
||||
def reach(self):
|
||||
return self._reach
|
||||
@@ -295,6 +298,15 @@ class InitialConditions(SQLSubModel):
|
||||
self._data.insert(index, n)
|
||||
self._status.modified()
|
||||
|
||||
def new_from_data(self, kp, discharge, elevation):
|
||||
n = Data(reach=self._reach, status=self._status)
|
||||
|
||||
n['kp'] = kp
|
||||
n['discharge'] = discharge
|
||||
n['elevation'] = elevation
|
||||
|
||||
return n
|
||||
|
||||
def insert(self, index, data):
|
||||
self._data.insert(index, data)
|
||||
self._status.modified()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
+14
-24
@@ -142,26 +142,21 @@ class Graph(object):
|
||||
def insert_node(self, node):
|
||||
return self._add_node(node)
|
||||
|
||||
def remove_node(self, node_name: str):
|
||||
self._nodes = list(
|
||||
filter(
|
||||
lambda n: n.name != node_name,
|
||||
self._nodes
|
||||
)
|
||||
)
|
||||
self._remove_associated_edge(node_name)
|
||||
def remove_node(self, node):
|
||||
self._nodes.remove(node)
|
||||
self._remove_associated_edge(node)
|
||||
self._status.modified()
|
||||
|
||||
def _remove_associated_edge(self, node_name: str):
|
||||
def _remove_associated_edge(self, node: str):
|
||||
edges = list(
|
||||
filter(
|
||||
lambda e: (e.node1.name == node_name or
|
||||
e.node2.name == node_name),
|
||||
lambda e: (e.node1 == node or
|
||||
e.node2 == node),
|
||||
self._edges,
|
||||
)
|
||||
)
|
||||
for edge in edges:
|
||||
self.remove_edge(edge.name)
|
||||
self.remove_edge(edge)
|
||||
|
||||
def create_node(self, x: float = 0.0, y: float = 0.0):
|
||||
node = self._create_node(x, y)
|
||||
@@ -176,11 +171,11 @@ class Graph(object):
|
||||
return edge
|
||||
|
||||
def _add_edge(self, edge):
|
||||
# This edge already exists ?
|
||||
if any(filter(lambda e: (e.node1 == edge.node1 and
|
||||
e.node2 == edge.node2),
|
||||
self._edges)):
|
||||
return None
|
||||
# # This edge already exists ?
|
||||
# if any(filter(lambda e: (e.node1 == edge.node1 and
|
||||
# e.node2 == edge.node2),
|
||||
# self._edges)):
|
||||
# return None
|
||||
|
||||
self._edges.append(edge)
|
||||
|
||||
@@ -197,13 +192,8 @@ class Graph(object):
|
||||
def create_edge(self, n1: Node, n2: Node):
|
||||
return self._create_edge(n1, n2)
|
||||
|
||||
def remove_edge(self, edge_name: str):
|
||||
self._edges = list(
|
||||
filter(
|
||||
lambda e: e.name != edge_name,
|
||||
self._edges
|
||||
)
|
||||
)
|
||||
def remove_edge(self, edge):
|
||||
self._edges.remove(edge)
|
||||
self._status.modified()
|
||||
|
||||
def is_upstream_node(self, node):
|
||||
|
||||
@@ -60,7 +60,7 @@ class GraphTestCase(unittest.TestCase):
|
||||
|
||||
e0 = g.add_edge(n0, n1)
|
||||
|
||||
g.remove_edge(e0.name)
|
||||
g.remove_edge(e0)
|
||||
|
||||
self.assertEqual(g.nodes_counts(), 2)
|
||||
self.assertEqual(g.edges_counts(), 0)
|
||||
@@ -73,7 +73,7 @@ class GraphTestCase(unittest.TestCase):
|
||||
|
||||
e0 = g.add_edge(n0, n1)
|
||||
|
||||
g.remove_node(n0.name)
|
||||
g.remove_node(n0)
|
||||
|
||||
self.assertEqual(g.nodes_counts(), 1)
|
||||
self.assertEqual(g.edges_counts(), 0)
|
||||
@@ -88,7 +88,7 @@ class GraphTestCase(unittest.TestCase):
|
||||
e0 = g.add_edge(n0, n1)
|
||||
e1 = g.add_edge(n1, n2)
|
||||
|
||||
g.remove_node(n1.name)
|
||||
g.remove_node(n1)
|
||||
|
||||
self.assertEqual(g.nodes_counts(), 2)
|
||||
self.assertEqual(g.edges_counts(), 0)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -126,3 +126,11 @@ class River(object):
|
||||
|
||||
self._reachs.append(new)
|
||||
return new
|
||||
|
||||
def get_reach_by_geometry(self, geometry_reach):
|
||||
return next(
|
||||
filter(
|
||||
lambda r: r.geometry is geometry_reach,
|
||||
self._reachs
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -378,6 +378,7 @@ class River(Graph, SQLSubModel):
|
||||
|
||||
def init_default(self):
|
||||
self.init_default_network()
|
||||
self.init_default_sediment()
|
||||
self.init_default_additional_files()
|
||||
|
||||
def init_default_network(self):
|
||||
@@ -386,6 +387,20 @@ class River(Graph, SQLSubModel):
|
||||
|
||||
e = self.add_edge(n1, n2)
|
||||
|
||||
def init_default_sediment(self):
|
||||
sediment = self._sediment_layers
|
||||
default = sediment.new(0)
|
||||
default_0 = default.new(0)
|
||||
|
||||
default.name = "default"
|
||||
default.comment = "Default sediment layers"
|
||||
|
||||
default_0.name = "L0"
|
||||
default_0.height = 1.0
|
||||
default_0.d50 = 0.002
|
||||
default_0.sigma = 1.0
|
||||
default_0.critical_constraint = 0.047
|
||||
|
||||
def init_default_additional_files(self):
|
||||
add_file = self._additional_files.new(0)
|
||||
add_file.name = "Pamhyr2 stamp file"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -71,6 +71,8 @@ class Study(SQLModel):
|
||||
lst = [
|
||||
StudyNetworkReachChecker(),
|
||||
StudyGeometryChecker(),
|
||||
StudyInitialConditionsChecker(),
|
||||
StudyBoundaryConditionChecker(),
|
||||
# DummyOK(),
|
||||
# DummyWARNING(),
|
||||
# DummyERROR(),
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
+70
-1
@@ -23,7 +23,20 @@ from enum import Flag, auto
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class Modules(Flag):
|
||||
class IterableFlag(Flag):
|
||||
def __iter__(self):
|
||||
all = filter(
|
||||
lambda v: v in self,
|
||||
type(self).values()
|
||||
)
|
||||
return all
|
||||
|
||||
@classmethod
|
||||
def values(cls):
|
||||
raise Exception("Not implemented yet")
|
||||
|
||||
|
||||
class Modules(IterableFlag):
|
||||
NONE = 0
|
||||
|
||||
# General
|
||||
@@ -48,6 +61,24 @@ class Modules(Flag):
|
||||
# Display
|
||||
WINDOW_LIST = auto()
|
||||
|
||||
@classmethod
|
||||
def values(cls):
|
||||
return [
|
||||
cls.STUDY, cls.CONFIG,
|
||||
cls.NETWORK,
|
||||
cls.GEOMETRY,
|
||||
cls.BOUNDARY_CONDITION,
|
||||
cls.LATERAL_CONTRIBUTION,
|
||||
cls.FRICTION,
|
||||
cls.INITIAL_CONDITION,
|
||||
cls.HYDRAULIC_STRUCTURES,
|
||||
cls.RESERVOIR,
|
||||
cls.SEDIMENT_LAYER,
|
||||
cls.ADDITIONAL_FILES,
|
||||
cls.RESULTS,
|
||||
cls.WINDOW_LIST,
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
return ~cls.NONE
|
||||
@@ -93,3 +124,41 @@ class Modules(Flag):
|
||||
cls.RESERVOIR: "Reservoir",
|
||||
cls.SEDIMENT_LAYER: "Sediment layer",
|
||||
}
|
||||
|
||||
def impact(self):
|
||||
res = Modules(0)
|
||||
for mod in self:
|
||||
if mod in _impact:
|
||||
for i in _impact[mod]:
|
||||
res |= i
|
||||
return res
|
||||
|
||||
def impact_set(self):
|
||||
res = []
|
||||
for mod in self:
|
||||
if mod in _impact:
|
||||
res += _impact[mod]
|
||||
|
||||
return set(res)
|
||||
|
||||
|
||||
_impact = {
|
||||
Modules.NETWORK: [
|
||||
Modules.GEOMETRY, Modules.BOUNDARY_CONDITION,
|
||||
Modules.LATERAL_CONTRIBUTION, Modules.FRICTION,
|
||||
Modules.RESERVOIR, Modules.SEDIMENT_LAYER,
|
||||
],
|
||||
Modules.GEOMETRY: [
|
||||
Modules.LATERAL_CONTRIBUTION, Modules.FRICTION,
|
||||
Modules.INITIAL_CONDITION, Modules.SEDIMENT_LAYER,
|
||||
],
|
||||
Modules.BOUNDARY_CONDITION: [
|
||||
Modules.SEDIMENT_LAYER
|
||||
],
|
||||
Modules.LATERAL_CONTRIBUTION: [],
|
||||
Modules.FRICTION: [],
|
||||
Modules.INITIAL_CONDITION: [],
|
||||
Modules.HYDRAULIC_STRUCTURES: [],
|
||||
Modules.RESERVOIR: [],
|
||||
Modules.SEDIMENT_LAYER: [],
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
+22
-18
@@ -492,6 +492,9 @@ class Mage(CommandLineSolver):
|
||||
if not hs.input_reach.is_enable():
|
||||
continue
|
||||
|
||||
if not hs.enabled:
|
||||
continue
|
||||
|
||||
if hs.input_kp is None:
|
||||
continue
|
||||
|
||||
@@ -507,25 +510,26 @@ class Mage(CommandLineSolver):
|
||||
|
||||
def _export_SIN_bhs(self, study, sin_dict, hs, f):
|
||||
for bhs in hs.basic_structures:
|
||||
reach_id = study.river.get_edge_id(hs.input_reach) + 1
|
||||
param_str = ' '.join(
|
||||
[
|
||||
f'{p:>10.3f}'
|
||||
for p in self._export_SIN_parameters(bhs)
|
||||
]
|
||||
)
|
||||
if bhs.enabled:
|
||||
reach_id = study.river.get_edge_id(hs.input_reach) + 1
|
||||
param_str = ' '.join(
|
||||
[
|
||||
f'{p:>10.3f}'
|
||||
for p in self._export_SIN_parameters(bhs)
|
||||
]
|
||||
)
|
||||
|
||||
name = bhs.name
|
||||
if name == "":
|
||||
name = f"HS_{bhs.id:>3}".replace(" ", "0")
|
||||
else:
|
||||
name = name.replace(" ", "_")
|
||||
name = bhs.name
|
||||
if name == "":
|
||||
name = f"HS_{bhs.id:>3}".replace(" ", "0")
|
||||
else:
|
||||
name = name.replace(" ", "_")
|
||||
|
||||
f.write(
|
||||
f"{sin_dict[bhs._type]} " +
|
||||
f"{reach_id} {float(hs.input_kp):>12.3f} " +
|
||||
f"{param_str} {name}\n"
|
||||
)
|
||||
f.write(
|
||||
f"{sin_dict[bhs._type]} " +
|
||||
f"{reach_id} {float(hs.input_kp):>12.3f} " +
|
||||
f"{param_str} {name}\n"
|
||||
)
|
||||
|
||||
def _export_SIN_parameters(self, bhs):
|
||||
res = [9999.999] * 5
|
||||
@@ -799,7 +803,7 @@ class Mage8(Mage):
|
||||
value = "O" if value.lower() == "y" else "N"
|
||||
|
||||
if name == "init_internal":
|
||||
value = ("p" if value.lower() in ["y", "yes", "true"]
|
||||
value = ("p" if value.lower() in ["y", "yes", "true", "o"]
|
||||
else "")
|
||||
|
||||
logger.debug(
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
# RubarBE.py -- Pamhyr
|
||||
# Copyright (C) 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 -*-
|
||||
|
||||
import os
|
||||
import logging
|
||||
import numpy as np
|
||||
|
||||
from tools import timer, trace, old_pamhyr_date_to_timestamp
|
||||
|
||||
from Solver.CommandLine import CommandLineSolver
|
||||
|
||||
from Model.Results.Results import Results
|
||||
from Model.Results.River.River import River, Reach, Profile
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class RubarBE(CommandLineSolver):
|
||||
_type = "rubarbe"
|
||||
|
||||
def __init__(self, name):
|
||||
super(RubarBE, self).__init__(name)
|
||||
|
||||
self._type = "rubarbe"
|
||||
|
||||
self._cmd_input = ""
|
||||
self._cmd_solver = "@path @input -o @output"
|
||||
self._cmd_output = ""
|
||||
|
||||
@classmethod
|
||||
def default_parameters(cls):
|
||||
lst = super(RubarBE, cls).default_parameters()
|
||||
|
||||
lst += [
|
||||
("rubarbe_cfl", "0.50000E+00"),
|
||||
("rubarbe_condam", "1"),
|
||||
("rubarbe_condav", "3"),
|
||||
("rubarbe_regime", "0"),
|
||||
("rubarbe_iodev", "n"),
|
||||
("rubarbe_iodebord", ""),
|
||||
("rubarbe_iostockage", ""),
|
||||
("rubarbe_iopdt", "y"),
|
||||
("rubarbe_iovis", "n"),
|
||||
("rubarbe_rep", "n"),
|
||||
("rubarbe_tinit", "000:00:00:00"),
|
||||
("rubarbe_tmax", "999:99:99:00"),
|
||||
("rubarbe_tiopdt", "000:00:00:00"),
|
||||
("rubarbe_dt", "3000.0"),
|
||||
("rubarbe_ts", "999:99:99:00"),
|
||||
("rubarbe_dtsauv", "999:99:99:00"),
|
||||
("rubarbe_psave", "999:99:99:00"),
|
||||
("rubarbe_fdeb1", "1"),
|
||||
("rubarbe_fdeb2", "10"),
|
||||
("rubarbe_fdeb3", "100"),
|
||||
("rubarbe_tf_1", "y"),
|
||||
("rubarbe_tf_2", "y"),
|
||||
("rubarbe_tf_3", "y"),
|
||||
("rubarbe_tf_4", "y"),
|
||||
("rubarbe_tf_5", "y"),
|
||||
("rubarbe_tf_6", "n"),
|
||||
("rubarbe_trased", "y"),
|
||||
("rubarbe_optfpc", "0"),
|
||||
("rubarbe_ros", "2650.0"),
|
||||
("rubarbe_dm", "0.1"),
|
||||
("rubarbe_segma", "1.0"),
|
||||
# Sediment parameters
|
||||
("rubarbe_sediment_ros", "2650.0"),
|
||||
("rubarbe_sediment_por", "0.4"),
|
||||
("rubarbe_sediment_dcharg", "0.0"),
|
||||
("rubarbe_sediment_halfa", "1.0"),
|
||||
("rubarbe_sediment_mult_1", "1.0"),
|
||||
("rubarbe_sediment_mult_2", ""),
|
||||
("rubarbe_sediment_mult_3", ""),
|
||||
("rubarbe_sediment_mult_4", ""),
|
||||
("rubarbe_sediment_mult_5", ""),
|
||||
("rubarbe_sediment_visc", "0.047"),
|
||||
("rubarbe_sediment_opts", "6"),
|
||||
("rubarbe_sediment_odchar", "0"),
|
||||
("rubarbe_sediment_unisol", "1"),
|
||||
("rubarbe_sediment_typdef", "3"),
|
||||
("rubarbe_sediment_depot", "2"),
|
||||
("rubarbe_sediment_choixc", "2"),
|
||||
("rubarbe_sediment_option", "2"),
|
||||
("rubarbe_sediment_capsol", "1"),
|
||||
("rubarbe_sediment_bmiu", "0.85"),
|
||||
("rubarbe_sediment_demix", "0"),
|
||||
("rubarbe_sediment_defond", "1"),
|
||||
("rubarbe_sediment_varcons", "1"),
|
||||
("rubarbe_sediment_dchard", "0.0"),
|
||||
("rubarbe_sediment_dchars", "0.0"),
|
||||
]
|
||||
|
||||
return lst
|
||||
|
||||
@classmethod
|
||||
def checkers(cls):
|
||||
lst = [
|
||||
]
|
||||
|
||||
return lst
|
||||
|
||||
##########
|
||||
# Export #
|
||||
##########
|
||||
|
||||
def cmd_args(self, study):
|
||||
lst = super(RubarBE, self).cmd_args(study)
|
||||
|
||||
return lst
|
||||
|
||||
def input_param(self):
|
||||
name = self._study.name
|
||||
return f"{name}.REP"
|
||||
|
||||
def output_param(self):
|
||||
name = self._study.name
|
||||
return f"{name}.BIN"
|
||||
|
||||
def log_file(self):
|
||||
name = self._study.name
|
||||
return f"{name}.TRA"
|
||||
|
||||
def export(self, study, repertory, qlog=None):
|
||||
self._study = study
|
||||
name = study.name.replace(" ", "_")
|
||||
|
||||
self._export_donnee(study, repertory, qlog, name=name)
|
||||
self._export_ts(study, repertory, qlog, name=name)
|
||||
self._export_geomac_i(study, repertory, qlog, name=name)
|
||||
self._export_mail(study, repertory, qlog, name=name)
|
||||
self._export_tps(study, repertory, qlog, name=name)
|
||||
self._export_stricklers(study, repertory, qlog, name=name)
|
||||
|
||||
def _export_donnee(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export DONNEE file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"donnee.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
params = filter(
|
||||
lambda p: "rubarbe_sediment_" not in p.name,
|
||||
study.river.get_params(self._type).parameters
|
||||
)
|
||||
|
||||
it = iter(params)
|
||||
|
||||
line = 0
|
||||
while line < 29:
|
||||
param = next(it)
|
||||
name = param.name
|
||||
value = param.value
|
||||
|
||||
if value != "":
|
||||
# Value format
|
||||
if value.count(':') == 3:
|
||||
value = old_pamhyr_date_to_timestamp(value)
|
||||
value = f"{value:>12.5e}".upper()
|
||||
|
||||
if value.count('.') == 1:
|
||||
value = f"{float(value):>12.5e}".upper()
|
||||
|
||||
if value == "y" or value == "n":
|
||||
value = "O" if value == "y" else "N"
|
||||
|
||||
# Write value
|
||||
f.write(f"{name:<50}{value}")
|
||||
|
||||
# Add values of 'rubarbe_iodebord' and
|
||||
# 'rubarbe_iostockage'
|
||||
if name == "rubarbe_iodev":
|
||||
v2 = next(it).value
|
||||
v3 = next(it).value
|
||||
|
||||
f.write(f"{v2}{v3}")
|
||||
|
||||
# New line
|
||||
f.write(f"\n")
|
||||
|
||||
line += 1
|
||||
|
||||
def _export_ts(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export TS file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"ts.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
def float_format(string):
|
||||
if "." in string:
|
||||
return f"{float(string):>10.0f}"
|
||||
return ""
|
||||
|
||||
params = filter(
|
||||
lambda p: "rubarbe_sediment_" in p.name,
|
||||
study.river.get_params(self.type).parameters
|
||||
)
|
||||
it = iter(params)
|
||||
|
||||
line = 0
|
||||
while line < 20:
|
||||
param = next(it)
|
||||
name = param.name
|
||||
value = param.value
|
||||
|
||||
if value != "":
|
||||
# Value format
|
||||
if value.count('.') == 1:
|
||||
value = f"{float_format(value)}"
|
||||
else:
|
||||
value = f"{value:>10}"
|
||||
|
||||
# Write value
|
||||
f.write(f"{name:<50}{value}")
|
||||
|
||||
# Add values of 'rubarbe_iodebord' and
|
||||
# 'rubarbe_iostockage'
|
||||
if name == "rubarbe_sediment_mult_1":
|
||||
m2 = f"{float_format(next(it).value)}"
|
||||
m3 = f"{float_format(next(it).value)}"
|
||||
m4 = f"{float_format(next(it).value)}"
|
||||
m5 = f"{float_format(next(it).value)}"
|
||||
|
||||
f.write(f"{m2}{m3}{m4}{m5}")
|
||||
|
||||
# New line
|
||||
f.write(f"\n")
|
||||
|
||||
line += 1
|
||||
|
||||
def _export_geomac_i(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export GEOMAC-i file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"geomac-i.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
n_profiles = len(reach)
|
||||
time = 0.0
|
||||
|
||||
f.write(f"{n_profiles:>5} {time:>11.3f}\n")
|
||||
|
||||
ind = 1
|
||||
for profile in reach.profiles:
|
||||
kp = profile.kp
|
||||
n_points = len(profile)
|
||||
|
||||
f.write(f"{ind:>4} {kp:>11.3f} {n_points:>4}\n")
|
||||
|
||||
for point in profile.points:
|
||||
label = point.name.lower()
|
||||
if label != "":
|
||||
if label[0] == "r":
|
||||
label = label[1].upper()
|
||||
else:
|
||||
label = lable[0]
|
||||
|
||||
y = point.y
|
||||
z = point.z
|
||||
dcs = 0.001
|
||||
scs = 1.0
|
||||
tmcs = 0.0
|
||||
|
||||
f.write(
|
||||
f"{label} {y:>11.5f}" +
|
||||
f"{z:>13.5f}{dcs:>15.10f}" +
|
||||
f"{scs:>15.10f}{tmcs:>15.5f}" +
|
||||
"\n"
|
||||
)
|
||||
|
||||
ind += 1
|
||||
|
||||
def _export_mail(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export MAIL file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"mail.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
lm = len(reach) + 1
|
||||
f.write(f"{lm:>13}\n")
|
||||
|
||||
for mails in [reach.inter_profiles_kp(),
|
||||
reach.get_kp()]:
|
||||
ind = 0
|
||||
for mail in mails:
|
||||
f.write(f"{mail:15.3f}")
|
||||
|
||||
ind += 1
|
||||
if ind % 3 == 0:
|
||||
f.write("\n")
|
||||
|
||||
if ind % 3 != 0:
|
||||
f.write("\n")
|
||||
|
||||
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="2")
|
||||
|
||||
def _export_frot(self, study, repertory, qlog, name="0", version=""):
|
||||
if qlog is not None:
|
||||
qlog.put(f"Export FROT{version} file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"frot{version}.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
lm = len(reach) + 1
|
||||
f.write(f"{lm:>6}\n")
|
||||
|
||||
def get_stricklers_from_kp(kp):
|
||||
return next(
|
||||
map(
|
||||
lambda s: (
|
||||
s.begin_strickler.medium if version == "2"
|
||||
else s.begin_strickler.minor
|
||||
),
|
||||
filter(
|
||||
lambda f: kp in f,
|
||||
edge.frictions.lst
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
ind = 1
|
||||
for mail in edge.reach.inter_profiles_kp():
|
||||
coef = get_stricklers_from_kp(mail)
|
||||
|
||||
f.write(f"{ind:>6} {coef:>12.5f}")
|
||||
|
||||
ind += 1
|
||||
f.write("\n")
|
||||
|
||||
def _export_tps(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export TPS file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"tps.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
|
||||
f.write(f"0.0\n")
|
||||
|
||||
ics = study.river.initial_conditions.get(edge)
|
||||
data = self._export_tps_init_data(ics)
|
||||
|
||||
profiles = reach.profiles
|
||||
first = profiles[0]
|
||||
last = profiles[-1]
|
||||
|
||||
if first.kp not in data or last.kp not in data:
|
||||
logger.error(
|
||||
"Study initial condition is not fully defined"
|
||||
)
|
||||
return
|
||||
|
||||
f_h_s = self._export_tps_profile_height_speed(first, data)
|
||||
l_h_s = self._export_tps_profile_height_speed(last, data)
|
||||
|
||||
# First mail
|
||||
f.write(f"{1:>5} {f_h_s[0]} {f_h_s[1]}")
|
||||
|
||||
ind = 2
|
||||
it = iter(profiles)
|
||||
prev = next(it)
|
||||
|
||||
prev_h, prev_s = f_h_s
|
||||
for profile in it:
|
||||
if profile.kp not in data:
|
||||
ind += 1
|
||||
continue
|
||||
|
||||
cur_h, cur_s = self._export_tps_profile_height_speed(
|
||||
profile, data
|
||||
)
|
||||
|
||||
# Mean of height and speed
|
||||
h = (prev_h + cur_h) / 2
|
||||
s = (prev_s + cur_s) / 2
|
||||
|
||||
f.write(f"{ind:>5} {h} {s}\n")
|
||||
|
||||
prev_h, prev_s = cur_h, cur_s
|
||||
ind += 1
|
||||
|
||||
# Last mail
|
||||
f.write(f"{ind:>5} {f_h_s[0]} {f_h_s[1]}")
|
||||
|
||||
def _export_tps_init_data(self, ics):
|
||||
data = {}
|
||||
|
||||
for d in ics.data:
|
||||
data[d['kp']] = (
|
||||
d['elevation'],
|
||||
d['discharge'],
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
def _export_tps_profile_height_speed(self, profile, data):
|
||||
z = data[profile.kp][0]
|
||||
q = data[profile.kp][1]
|
||||
|
||||
height = z - profile.z_min()
|
||||
speed = profile.speed(q, z)
|
||||
|
||||
return height, speed
|
||||
@@ -22,6 +22,7 @@ from Solver.GenericSolver import GenericSolver
|
||||
from Solver.Mage import (
|
||||
Mage7, Mage8, MageFake7,
|
||||
)
|
||||
from Solver.RubarBE import RubarBE
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
@@ -30,6 +31,7 @@ solver_long_name = {
|
||||
# "mage7": "Mage v7",
|
||||
"mage8": "Mage v8",
|
||||
# "mage_fake7": "Mage fake v7",
|
||||
# "rubarbe": "RubarBE",
|
||||
}
|
||||
|
||||
solver_type_list = {
|
||||
@@ -37,4 +39,5 @@ solver_type_list = {
|
||||
# "mage7": Mage7,
|
||||
"mage8": Mage8,
|
||||
# "mage_fake7": MageFake7,
|
||||
# "rubarbe": RubarBE,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -67,5 +67,5 @@ class AboutWindow(PamhyrDialog):
|
||||
label = f"\n - {author}" + label
|
||||
except StopIteration:
|
||||
label = _translate("About", "Contributors: ") + label
|
||||
label = "Copyright © 2023-2024 INRAE\n" + label
|
||||
label = "Copyright © 2022-2024 INRAE\n" + label
|
||||
self.set_label_text("label_copyright", label)
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -48,7 +48,7 @@ class BCTranslate(MainTranslate):
|
||||
|
||||
self._sub_dict["long_types"] = {
|
||||
"ND": self._dict["not_defined"],
|
||||
"PC": _translate("BoundaryCondition", "Ponctual contribution"),
|
||||
"PC": _translate("BoundaryCondition", "Point sources"),
|
||||
"TZ": _translate("BoundaryCondition", "Z(t)"),
|
||||
"TD": _translate("BoundaryCondition", "Q(t)"),
|
||||
"ZD": _translate("BoundaryCondition", "Q(Z)"),
|
||||
|
||||
@@ -46,7 +46,7 @@ class Worker(QObject):
|
||||
self.signalStatus.emit(checker.name)
|
||||
|
||||
# Run checker
|
||||
checker._run(self._study)
|
||||
checker.run(self._study)
|
||||
|
||||
self.signalStatus.emit("progress")
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -42,7 +42,7 @@ class PlotStricklers(PamhyrPlot):
|
||||
)
|
||||
|
||||
self.label_x = self._trad["kp"]
|
||||
self.label_y = self._trad["stricklers"]
|
||||
self.label_y = self._trad["strickler_plot"]
|
||||
|
||||
self.line_kp_elevation = [None, None]
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ class FrictionsWindow(PamhyrWindow):
|
||||
self.plot = PlotKPZ(
|
||||
canvas=self.canvas,
|
||||
data=self._reach.reach,
|
||||
study=self._study,
|
||||
trad=self._trad,
|
||||
toolbar=None,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -28,6 +28,9 @@ class FrictionsTranslate(MainTranslate):
|
||||
super(FrictionsTranslate, self).__init__()
|
||||
|
||||
self._dict["kp"] = self._dict["unit_kp"]
|
||||
self._dict["strickler_plot"] = _translate(
|
||||
"Frictions", "Strickler ($m^{1/3}/s$)"
|
||||
)
|
||||
self._dict["stricklers"] = _translate(
|
||||
"Frictions", "Stricklers"
|
||||
)
|
||||
@@ -39,8 +42,8 @@ class FrictionsTranslate(MainTranslate):
|
||||
self._sub_dict["table_headers"] = {
|
||||
# "name": self._dict["name"],
|
||||
# "edge": self._dict["reach"],
|
||||
"begin_kp": _translate("Frictions", "Begin kp (m)"),
|
||||
"end_kp": _translate("Frictions", "End kp (m)"),
|
||||
"begin_strickler": _translate("Frictions", "Begin strickler"),
|
||||
"end_strickler": _translate("Frictions", "End strickler"),
|
||||
"begin_kp": _translate("Frictions", "Start (m)"),
|
||||
"end_kp": _translate("Frictions", "End (m)"),
|
||||
"begin_strickler": _translate("Frictions", "Start coefficient"),
|
||||
"end_strickler": _translate("Frictions", "End coefficient"),
|
||||
}
|
||||
@@ -58,6 +58,7 @@ class MeshingDialog(PamhyrDialog):
|
||||
self._end_cs = -1
|
||||
self._begin_dir = "un"
|
||||
self._end_dir = "np"
|
||||
self._origin = self._reach.profile(0)
|
||||
|
||||
self._init_default_values_profiles()
|
||||
self._init_default_values_guidelines()
|
||||
@@ -67,17 +68,6 @@ class MeshingDialog(PamhyrDialog):
|
||||
self._space_step
|
||||
)
|
||||
|
||||
lm_dict = self._trad.get_dict("lm_dict")
|
||||
self.combobox_add_items(
|
||||
"comboBox_lm", list(
|
||||
map(
|
||||
lambda x: lm_dict[x],
|
||||
["1", "2", "3"]
|
||||
)
|
||||
)
|
||||
)
|
||||
self.set_combobox_text("comboBox_lm", self._lm)
|
||||
|
||||
if self._linear:
|
||||
self.set_radio_button("radioButton_linear", True)
|
||||
else:
|
||||
@@ -118,8 +108,8 @@ class MeshingDialog(PamhyrDialog):
|
||||
gl, _ = self._reach.compute_guidelines()
|
||||
gl = list(gl)
|
||||
|
||||
bgl = ['un'] + gl
|
||||
egl = gl + ['np']
|
||||
bgl = ['un'] + gl + ['np']
|
||||
egl = ['un'] + gl + ['np']
|
||||
|
||||
self.combobox_add_items("comboBox_begin_gl", bgl)
|
||||
self.combobox_add_items("comboBox_end_gl", egl)
|
||||
@@ -135,21 +125,17 @@ class MeshingDialog(PamhyrDialog):
|
||||
def lplan(self):
|
||||
return self._lplan
|
||||
|
||||
@property
|
||||
def lm(self):
|
||||
return int(self._lm)
|
||||
|
||||
@property
|
||||
def linear(self):
|
||||
return self._linear
|
||||
|
||||
@property
|
||||
def begin_cs(self):
|
||||
return self._begin_cs + 1
|
||||
return self._begin_cs
|
||||
|
||||
@property
|
||||
def end_cs(self):
|
||||
return self._end_cs + 1
|
||||
return self._end_cs
|
||||
|
||||
@property
|
||||
def begin_dir(self):
|
||||
@@ -174,9 +160,6 @@ class MeshingDialog(PamhyrDialog):
|
||||
self._begin_dir = self.get_combobox_text("comboBox_begin_gl")
|
||||
self._end_dir = self.get_combobox_text("comboBox_end_gl")
|
||||
|
||||
r_lm_dict = self._trad.get_dict("r_lm_dict")
|
||||
self._lm = r_lm_dict[self.get_combobox_text("comboBox_lm")]
|
||||
|
||||
super().accept()
|
||||
|
||||
def reject(self):
|
||||
|
||||
@@ -98,6 +98,7 @@ class PlotAC(PamhyrPlot):
|
||||
label=self.label_next_plot_selected,
|
||||
color=self.color_plot_next, lw=1.6, linestyle='--'
|
||||
)
|
||||
self.canvas.axes.set_visible(False)
|
||||
|
||||
def draw_gl(self):
|
||||
if self._current_data_update:
|
||||
@@ -106,12 +107,16 @@ class PlotAC(PamhyrPlot):
|
||||
profile_id = 0
|
||||
|
||||
profile = self.data.profile(profile_id)
|
||||
if profile is None:
|
||||
return
|
||||
|
||||
station = profile.get_station()
|
||||
elevation = profile.z()
|
||||
gl = profile.names()
|
||||
|
||||
self.annotation = []
|
||||
self.complete_gl, self.incomplete_gl = self.data.compute_guidelines()
|
||||
|
||||
lcomplete = list(self.complete_gl)
|
||||
lincomplete = list(self.incomplete_gl)
|
||||
|
||||
@@ -129,7 +134,7 @@ class PlotAC(PamhyrPlot):
|
||||
|
||||
if txt.strip() in self.complete_gl:
|
||||
color = self.color_complete_gl[
|
||||
lcomplete.index(txt)
|
||||
lcomplete.index(txt) % len(self.color_complete_gl)
|
||||
]
|
||||
else:
|
||||
color = self.color_incomplete_gl[
|
||||
@@ -160,6 +165,9 @@ class PlotAC(PamhyrPlot):
|
||||
self.draw()
|
||||
return
|
||||
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
self.update_current()
|
||||
self.update_gl()
|
||||
|
||||
@@ -171,6 +179,10 @@ class PlotAC(PamhyrPlot):
|
||||
previous_id = profile_id - 1
|
||||
next_id = profile_id + 1
|
||||
|
||||
self.previous_plot_selected.set_visible(False)
|
||||
self.plot_selected.set_visible(False)
|
||||
self.next_plot_selected.set_visible(False)
|
||||
|
||||
self.previous_plot_selected.set_data([], [])
|
||||
self.plot_selected.set_data([], [])
|
||||
self.next_plot_selected.set_data([], [])
|
||||
@@ -180,18 +192,23 @@ class PlotAC(PamhyrPlot):
|
||||
self.data.profile(previous_id).get_station(),
|
||||
self.data.profile(previous_id).z()
|
||||
)
|
||||
self.previous_plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= profile_id < self.data.number_profiles:
|
||||
self.plot_selected.set_data(
|
||||
self.data.profile(profile_id).get_station(),
|
||||
self.data.profile(profile_id).z()
|
||||
)
|
||||
self.plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= next_id < self.data.number_profiles:
|
||||
self.next_plot_selected.set_data(
|
||||
self.data.profile(next_id).get_station(),
|
||||
self.data.profile(next_id).z()
|
||||
)
|
||||
self.next_plot_selected.set_visible(True)
|
||||
|
||||
self.canvas.axes.set_visible(True)
|
||||
|
||||
def update_full(self):
|
||||
self.draw()
|
||||
|
||||
@@ -25,8 +25,11 @@ logger = logging.getLogger()
|
||||
|
||||
|
||||
class PlotKPZ(PamhyrPlot):
|
||||
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
|
||||
def __init__(self, canvas=None, trad=None,
|
||||
study=None, data=None, toolbar=None,
|
||||
parent=None):
|
||||
self._study = study
|
||||
|
||||
super(PlotKPZ, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
@@ -75,6 +78,7 @@ class PlotKPZ(PamhyrPlot):
|
||||
self.draw_current()
|
||||
self.draw_gl()
|
||||
self.draw_bottom()
|
||||
self.draw_profiles_hs(self._data)
|
||||
|
||||
self.idle()
|
||||
self._init = True
|
||||
@@ -167,7 +171,9 @@ class PlotKPZ(PamhyrPlot):
|
||||
|
||||
self.line_kp_zgl.append(
|
||||
self.canvas.axes.plot(
|
||||
kp, z, lw=1., color=self.colors[ind]
|
||||
kp, z, lw=1.,
|
||||
color=self.colors[ind % len(self.colors)],
|
||||
linestyle=self.linestyle[ind // len(self.colors)]
|
||||
)
|
||||
)
|
||||
ind += 1
|
||||
@@ -182,6 +188,37 @@ class PlotKPZ(PamhyrPlot):
|
||||
color='lightgrey'
|
||||
)
|
||||
|
||||
def draw_profiles_hs(self, reach):
|
||||
lhs = filter(
|
||||
lambda hs: hs._input_reach.reach is reach,
|
||||
filter(
|
||||
lambda hs: hs._input_reach is not None,
|
||||
self._study.river.hydraulic_structures.lst
|
||||
)
|
||||
)
|
||||
|
||||
for hs in lhs:
|
||||
x = hs.input_kp
|
||||
z_min = reach.get_z_min()
|
||||
z_max = reach.get_z_max()
|
||||
|
||||
self.canvas.axes.plot(
|
||||
[x, x],
|
||||
[min(z_min), max(z_max)],
|
||||
linestyle="--",
|
||||
lw=1.,
|
||||
color=self.color_plot_previous,
|
||||
)
|
||||
|
||||
self.canvas.axes.annotate(
|
||||
" > " + hs.name,
|
||||
(x, max(z_max)),
|
||||
horizontalalignment='left',
|
||||
verticalalignment='top',
|
||||
annotation_clip=True,
|
||||
fontsize=9, color=self.color_plot_previous,
|
||||
)
|
||||
|
||||
@timer
|
||||
def update(self):
|
||||
if not self._init:
|
||||
|
||||
@@ -61,6 +61,7 @@ class PlotXY(PamhyrPlot):
|
||||
return
|
||||
|
||||
self.draw_xy()
|
||||
self.draw_lr()
|
||||
self.draw_gl()
|
||||
self.draw_current()
|
||||
|
||||
@@ -90,6 +91,37 @@ class PlotXY(PamhyrPlot):
|
||||
)
|
||||
self.line_xy.append(line)
|
||||
|
||||
def draw_lr(self):
|
||||
lx = []
|
||||
ly = []
|
||||
rx = []
|
||||
ry = []
|
||||
|
||||
self.line_lr = []
|
||||
for x, y in zip(self.data.get_x(),
|
||||
self.data.get_y()):
|
||||
lx.append(x[0])
|
||||
ly.append(y[0])
|
||||
|
||||
rx.append(x[-1])
|
||||
ry.append(y[-1])
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
lx, ly,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
rx, ry,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
def draw_gl(self):
|
||||
x_complete = self.data.get_guidelines_x()
|
||||
y_complete = self.data.get_guidelines_y()
|
||||
@@ -98,7 +130,8 @@ class PlotXY(PamhyrPlot):
|
||||
self.line_gl = []
|
||||
for x, y in zip(x_complete, y_complete):
|
||||
line = self.canvas.axes.plot(
|
||||
x, y, color=self.colors[ind]
|
||||
x, y, color=self.colors[ind % len(self.colors)],
|
||||
linestyle=self.linestyle[ind // len(self.colors)]
|
||||
)
|
||||
self.line_gl.append(line)
|
||||
ind += 1
|
||||
@@ -139,6 +172,7 @@ class PlotXY(PamhyrPlot):
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
self.update_lr()
|
||||
self.update_gl()
|
||||
self.update_current()
|
||||
|
||||
@@ -207,3 +241,9 @@ class PlotXY(PamhyrPlot):
|
||||
self.data.profile(after).y()
|
||||
)
|
||||
self.after_plot_selected.set_visible(True)
|
||||
|
||||
def update_lr(self):
|
||||
for line in self.line_lr:
|
||||
line[0].remove()
|
||||
|
||||
self.draw_lr()
|
||||
@@ -18,14 +18,17 @@
|
||||
|
||||
import logging
|
||||
|
||||
from math import dist, sqrt
|
||||
|
||||
from tools import timer, trace
|
||||
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||
from View.Tools.Plot.OnPickEvent import OnpickEvent
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QCoreApplication
|
||||
Qt, QCoreApplication, QItemSelectionModel
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
@@ -43,6 +46,9 @@ class Plot(PamhyrPlot):
|
||||
)
|
||||
|
||||
self._table = table
|
||||
self._z_note = None
|
||||
self._z_line = None
|
||||
self._z_fill_between = None
|
||||
|
||||
self.line_xy = []
|
||||
self.line_gl = []
|
||||
@@ -56,6 +62,138 @@ class Plot(PamhyrPlot):
|
||||
|
||||
self._isometric_axis = False
|
||||
|
||||
self.hl_points = []
|
||||
self.highlight = (
|
||||
[], # Points list to highlight
|
||||
None # Hydrolic values (z, wet_area,
|
||||
# wet_preimeter, water_width)
|
||||
)
|
||||
|
||||
def onpick(self, event):
|
||||
if event.mouseevent.inaxes != self.canvas.axes:
|
||||
return
|
||||
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
if modifiers != Qt.ControlModifier:
|
||||
return
|
||||
|
||||
_, hyd = self.highlight
|
||||
|
||||
ind, point = self._closer_point(event)
|
||||
|
||||
self.highlight = ([point], hyd)
|
||||
self._select_in_table(ind)
|
||||
|
||||
self.update()
|
||||
return
|
||||
|
||||
def onclick(self, event):
|
||||
if event.inaxes != self.canvas.axes:
|
||||
return
|
||||
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
if modifiers != Qt.ShiftModifier:
|
||||
return
|
||||
|
||||
points, _ = self.highlight
|
||||
|
||||
z = self._get_z_from_click(event)
|
||||
if z < self.data.z_min():
|
||||
return
|
||||
|
||||
a, p, w = self._compute_hydrolics(z)
|
||||
|
||||
logger.debug(f"{z, a, p, w}")
|
||||
|
||||
self.highlight = (points, (z, a, p, w))
|
||||
|
||||
self.update()
|
||||
return
|
||||
|
||||
def select_points_from_indexes(self, indexes):
|
||||
data = self.data
|
||||
_, hyd = self.highlight
|
||||
|
||||
points = list(
|
||||
map(
|
||||
lambda e: e[1],
|
||||
filter(
|
||||
lambda e: e[0] in indexes,
|
||||
enumerate(
|
||||
zip(data.get_station(), data.z())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.highlight = (points, hyd)
|
||||
self.update()
|
||||
|
||||
def _select_in_table(self, ind):
|
||||
if self._table is not None:
|
||||
self._table.blockSignals(True)
|
||||
self._table.setFocus()
|
||||
|
||||
selection = self._table.selectionModel()
|
||||
index = self._table.model().index(ind, 0)
|
||||
selection.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
self._table.scrollTo(index)
|
||||
self._table.blockSignals(False)
|
||||
|
||||
def _closer_point(self, event):
|
||||
points_ind = event.ind
|
||||
axes = self.canvas.axes
|
||||
bx, by = axes.get_xlim(), axes.get_ylim()
|
||||
ratio = (bx[0] - bx[1]) / (by[0] - by[1])
|
||||
|
||||
x = event.artist.get_xdata()
|
||||
y = event.artist.get_ydata()
|
||||
|
||||
# points = filter(
|
||||
# lambda e: e[0] in points_ind,
|
||||
# enumerate(zip(x, y))
|
||||
# )
|
||||
points = enumerate(zip(x, y))
|
||||
|
||||
mx = event.mouseevent.xdata
|
||||
my = event.mouseevent.ydata
|
||||
|
||||
def dist_mouse(point):
|
||||
x, y = point[1]
|
||||
d = sqrt(
|
||||
(((mx - x) / ratio) ** 2) +
|
||||
((my - y) ** 2)
|
||||
)
|
||||
return d
|
||||
|
||||
closer = min(
|
||||
points, key=dist_mouse
|
||||
)
|
||||
|
||||
return closer
|
||||
|
||||
def _get_z_from_click(self, event):
|
||||
return event.ydata
|
||||
|
||||
def _compute_hydrolics(self, z):
|
||||
profile = self.data
|
||||
|
||||
points = profile.wet_points(z)
|
||||
station = profile._get_station(points)
|
||||
width = abs(station[0] - station[-1])
|
||||
|
||||
poly = profile.wet_polygon(z)
|
||||
area = poly.area
|
||||
perimeter = poly.length
|
||||
|
||||
return area, perimeter, width
|
||||
|
||||
@timer
|
||||
def draw(self):
|
||||
self.init_axes()
|
||||
@@ -65,19 +203,24 @@ class Plot(PamhyrPlot):
|
||||
x_carto = self.data.x()
|
||||
y_carto = self.data.y()
|
||||
|
||||
if (len(x_carto) < 3 or len(y_carto) < 3 or
|
||||
len(x) < 3):
|
||||
if (len(x_carto) < 3 or len(y_carto) < 3 or len(x) < 3):
|
||||
# Noting to do in this case
|
||||
return
|
||||
|
||||
gl = map(lambda p: p.name, self.data.points)
|
||||
|
||||
self.profile_line2D, = self.canvas.axes.plot(
|
||||
x, y, color=self.color_plot,
|
||||
lw=1.5, markersize=7, marker='+',
|
||||
picker=30
|
||||
)
|
||||
|
||||
self.draw_annotation(x, y)
|
||||
self.draw_highligth()
|
||||
|
||||
self.idle()
|
||||
|
||||
def draw_annotation(self, x, y):
|
||||
gl = map(lambda p: p.name, self.data.points)
|
||||
|
||||
# Add label on graph
|
||||
self.annotation = []
|
||||
for i, name in enumerate(list(gl)):
|
||||
@@ -113,30 +256,80 @@ class Plot(PamhyrPlot):
|
||||
self.canvas.axes.set_facecolor('#F9F9F9')
|
||||
self.canvas.figure.patch.set_facecolor('white')
|
||||
|
||||
self.onpick_event = OnpickEvent(
|
||||
self.canvas.axes,
|
||||
x, y, x_carto, y_carto,
|
||||
self._table
|
||||
)
|
||||
self.canvas.figure.canvas\
|
||||
.mpl_connect(
|
||||
'pick_event',
|
||||
self.onpick_event.onpick
|
||||
)
|
||||
def draw_highligth(self):
|
||||
points, hyd = self.highlight
|
||||
for p in self.hl_points:
|
||||
p[0].set_data([], [])
|
||||
|
||||
self.onclick_event = OnpickEvent(
|
||||
self.canvas.axes,
|
||||
x, y, x_carto, y_carto,
|
||||
self._table
|
||||
)
|
||||
self.canvas.figure.canvas\
|
||||
.mpl_connect(
|
||||
'button_press_event',
|
||||
self.onclick_event.onclick
|
||||
)
|
||||
self.hl_points = []
|
||||
|
||||
self.idle()
|
||||
for x, y in points:
|
||||
self.hl_points.append(
|
||||
self.canvas.axes.plot(
|
||||
x, y,
|
||||
color=self.color_plot_highlight,
|
||||
lw=1.5, markersize=7, marker='+',
|
||||
picker=30
|
||||
)
|
||||
)
|
||||
|
||||
if hyd is not None:
|
||||
self.draw_highligth_z_line(*hyd)
|
||||
|
||||
def draw_highligth_z_line(self, z, a, p, w):
|
||||
text = (
|
||||
f"Z = {z:.3f} m, " +
|
||||
f"{self._trad['width']} = {w:.3f} m,\n" +
|
||||
f"{self._trad['area']} = {a:.3f} m², " +
|
||||
f"{self._trad['perimeter']} = {p:.3f} m"
|
||||
)
|
||||
|
||||
x = self.data.get_station()
|
||||
xlim = (x[0], x[-1])
|
||||
pos = (
|
||||
xlim[0] + (abs(xlim[0] - xlim[1]) * 0.05),
|
||||
z + 0.8
|
||||
)
|
||||
y = self.data.z()
|
||||
|
||||
if self._z_note is None:
|
||||
self.draw_highligth_z_line_fill(x, y, z)
|
||||
|
||||
self._z_line = self.canvas.axes.plot(
|
||||
xlim, [z, z],
|
||||
color=self.color_plot_river_water
|
||||
)
|
||||
|
||||
self._z_note = self.canvas.axes.annotate(
|
||||
text, pos,
|
||||
horizontalalignment='left',
|
||||
verticalalignment='top',
|
||||
annotation_clip=True,
|
||||
color=self.color_plot_river_water,
|
||||
fontsize=9,
|
||||
fontweight='bold',
|
||||
alpha=0.7
|
||||
)
|
||||
else:
|
||||
self.draw_highligth_z_line_fill(x, y, z)
|
||||
|
||||
self._z_line[0].set_data(xlim, [z, z])
|
||||
self._z_note.set_position(pos)
|
||||
self._z_note.set_text(text)
|
||||
|
||||
def draw_highligth_z_line_fill(self, x, y, z):
|
||||
if self._z_fill_between is not None:
|
||||
self._z_fill_between.remove()
|
||||
|
||||
self._z_fill_between = self.canvas.axes.fill_between(
|
||||
x, y, z,
|
||||
where=y <= z,
|
||||
facecolor=self.color_plot_river_water_zone,
|
||||
interpolate=True, alpha=0.7
|
||||
)
|
||||
|
||||
@timer
|
||||
def update(self):
|
||||
self.draw()
|
||||
self.draw_highligth()
|
||||
|
||||
self.update_idle()
|
||||
@@ -82,16 +82,6 @@ class GeometryProfileTableModel(PamhyrTableModel):
|
||||
elif value == self._data.z_max():
|
||||
return QColor("blue")
|
||||
|
||||
if role == Qt.ToolTipRole:
|
||||
if value == self._data.z_min():
|
||||
return _translate("MainWindowProfile",
|
||||
"La cote du fond",
|
||||
"Z minimale")
|
||||
elif value == self._data.z_max():
|
||||
return _translate("MainWindowProfile",
|
||||
"La cote maximale",
|
||||
"Z maximale")
|
||||
|
||||
if index.column() == 3:
|
||||
value = self._data.point(index.row()).name
|
||||
|
||||
@@ -104,16 +94,6 @@ class GeometryProfileTableModel(PamhyrTableModel):
|
||||
if role == Qt.ForegroundRole:
|
||||
return QColor("darkRed")
|
||||
|
||||
if role == Qt.ToolTipRole:
|
||||
if value.strip().upper() == "RG":
|
||||
return _translate(
|
||||
"MainWindowProfile", "Rive gauche"
|
||||
)
|
||||
else:
|
||||
return _translate(
|
||||
"MainWindowProfile", "Rive droite"
|
||||
)
|
||||
|
||||
return QVariant()
|
||||
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
@@ -250,6 +230,26 @@ class GeometryProfileTableModel(PamhyrTableModel):
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def purge(self, np_purge):
|
||||
|
||||
self._undo.push(
|
||||
PurgeCommand(
|
||||
self._data, np_purge
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def reverse(self):
|
||||
|
||||
self._undo.push(
|
||||
ReverseCommand(
|
||||
self._data
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def paste(self, row, header, data):
|
||||
if row > self._data.number_points:
|
||||
return
|
||||
|
||||
@@ -32,6 +32,10 @@ class GeometryProfileTranslate(GeometryTranslate):
|
||||
"Geometry", "Geometry cross-section"
|
||||
)
|
||||
|
||||
self._dict["width"] = _translate("Geometry", "Width")
|
||||
self._dict["area"] = _translate("Geometry", "Area")
|
||||
self._dict["perimeter"] = _translate("Geometry", "Perimeter")
|
||||
|
||||
self._sub_dict["table_headers"] = {
|
||||
"x": self._dict["x"],
|
||||
"y": self._dict["y"],
|
||||
|
||||
@@ -169,6 +169,34 @@ class MoveCommand(QUndoCommand):
|
||||
self._profile.move_down_point(self._i)
|
||||
|
||||
|
||||
class ReverseCommand(QUndoCommand):
|
||||
def __init__(self, profile):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._profile = profile
|
||||
|
||||
def undo(self):
|
||||
self._profile.reverse()
|
||||
|
||||
def redo(self):
|
||||
self._profile.reverse()
|
||||
|
||||
|
||||
class PurgeCommand(QUndoCommand):
|
||||
def __init__(self, profile, np_purge):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._profile = profile
|
||||
self._old = self._profile.points.copy()
|
||||
self._np_purge = np_purge
|
||||
|
||||
def undo(self):
|
||||
self._profile._points = self._old.copy()
|
||||
|
||||
def redo(self):
|
||||
self._profile.purge(self._np_purge)
|
||||
|
||||
|
||||
class PasteCommand(QUndoCommand):
|
||||
def __init__(self, profile, row, points):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
@@ -20,8 +20,9 @@ import copy
|
||||
import sys
|
||||
import csv
|
||||
from time import time
|
||||
import logging
|
||||
|
||||
from tools import trace, timer
|
||||
from tools import trace, timer, logger_exception
|
||||
from Modules import Modules
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
@@ -46,9 +47,12 @@ from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||
from View.Geometry.Profile.Plot import Plot
|
||||
from View.Geometry.Profile.Table import GeometryProfileTableModel
|
||||
from View.Geometry.Profile.Translate import GeometryProfileTranslate
|
||||
from View.Geometry.PurgeDialog import PurgeDialog
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class ProfileWindow(PamhyrWindow):
|
||||
_pamhyr_ui = "GeometryCrossSection"
|
||||
@@ -125,26 +129,49 @@ class ProfileWindow(PamhyrWindow):
|
||||
"action_down": self.move_down,
|
||||
"action_add": self.add,
|
||||
"action_delete": self.delete,
|
||||
"action_purge": self.purge,
|
||||
"action_reverse": self.reverse,
|
||||
}
|
||||
|
||||
for action in actions:
|
||||
self.find(QAction, action)\
|
||||
.triggered.connect(actions[action])
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
table.selectionModel()\
|
||||
.selectionChanged\
|
||||
.connect(self.update_points_selection)
|
||||
|
||||
self._tablemodel.dataChanged.connect(self.update)
|
||||
|
||||
def update_points_selection(self):
|
||||
rows = self.index_selected_rows()
|
||||
self._plot.select_points_from_indexes(rows)
|
||||
|
||||
def update(self):
|
||||
self.update_plot()
|
||||
self._propagate_update(key=Modules.GEOMETRY)
|
||||
|
||||
def _update(self, redraw=False, propagate=True):
|
||||
if redraw:
|
||||
self.update_plot()
|
||||
if propagate:
|
||||
self._propagate_update(key=Modules.GEOMETRY)
|
||||
|
||||
def update_plot(self):
|
||||
self._tablemodel.blockSignals(True)
|
||||
|
||||
# TODO: Do not rebuild all graph
|
||||
self._plot.update()
|
||||
|
||||
self._tablemodel.blockSignals(False)
|
||||
|
||||
def _propagated_update(self, key=Modules(0)):
|
||||
if Modules.GEOMETRY not in key:
|
||||
return
|
||||
|
||||
self._tablemodel.layoutChanged.emit()
|
||||
self._update(redraw=True, propagate=False)
|
||||
|
||||
def index_selected_row(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
rows = table.selectionModel()\
|
||||
@@ -154,6 +181,15 @@ class ProfileWindow(PamhyrWindow):
|
||||
|
||||
return rows[0].row()
|
||||
|
||||
def index_selected_rows(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
return list(
|
||||
map(
|
||||
lambda r: r.row(),
|
||||
table.selectionModel().selectedRows()
|
||||
)
|
||||
)
|
||||
|
||||
def add(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
if len(table.selectedIndexes()) == 0:
|
||||
@@ -221,6 +257,27 @@ class ProfileWindow(PamhyrWindow):
|
||||
|
||||
self.update()
|
||||
|
||||
def purge(self):
|
||||
self._tablemodel.purge()
|
||||
self.update()
|
||||
|
||||
def purge(self):
|
||||
try:
|
||||
dlg = PurgeDialog(
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
if dlg.exec():
|
||||
self._tablemodel.purge(dlg.np_purge)
|
||||
self._plot.draw()
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return
|
||||
|
||||
def reverse(self):
|
||||
self._tablemodel.reverse()
|
||||
self.update()
|
||||
|
||||
def _copy(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
rows = table.selectionModel().selectedRows()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -0,0 +1,60 @@
|
||||
# PurgeDialog.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.QtGui import (
|
||||
QKeySequence,
|
||||
)
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QVariant, QAbstractTableModel,
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QUndoStack, QShortcut, QSpinBox,
|
||||
)
|
||||
|
||||
|
||||
class PurgeDialog(PamhyrDialog):
|
||||
_pamhyr_ui = "PurgeOptions"
|
||||
_pamhyr_name = "Purge"
|
||||
|
||||
def __init__(self, trad=None, parent=None):
|
||||
super(PurgeDialog, self).__init__(
|
||||
title=trad[self._pamhyr_name],
|
||||
trad=trad,
|
||||
options=[],
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._init_default_values()
|
||||
|
||||
def _init_default_values(self):
|
||||
self._np_purge = 24
|
||||
|
||||
@property
|
||||
def np_purge(self):
|
||||
return self._np_purge
|
||||
|
||||
def accept(self):
|
||||
self._np_purge = self.get_spin_box("spinBox_np_purge")
|
||||
super().accept()
|
||||
|
||||
def reject(self):
|
||||
self.close()
|
||||
@@ -240,9 +240,30 @@ class GeometryReachTableModel(PamhyrTableModel):
|
||||
|
||||
self._undo.push(
|
||||
MeshingCommand(
|
||||
self._data, mesher, data
|
||||
self._data, mesher, data, "mesh"
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def update_kp(self, mesher, data):
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
self._undo.push(
|
||||
MeshingCommand(
|
||||
self._data, mesher, data, "update_kp"
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def purge(self, np_purge):
|
||||
|
||||
self._undo.push(
|
||||
PurgeCommand(
|
||||
self._data, np_purge
|
||||
)
|
||||
)
|
||||
self.layoutChanged.emit()
|
||||
@@ -33,10 +33,12 @@ class GeometryTranslate(MainTranslate):
|
||||
|
||||
self._dict["open_file"] = _translate("Geometry", "Open a file")
|
||||
self._dict["file_st"] = _translate(
|
||||
"Geometry", "File mage geometry (*.ST *.st)")
|
||||
"Geometry", "Mage geometry file (*.ST *.st)")
|
||||
self._dict["file_m"] = _translate(
|
||||
"Geometry", "File mage meshed geometry (*.M *.m)")
|
||||
self._dict["file_all"] = _translate("Geometry", "All file (*)")
|
||||
"Geometry", "Mage meshed geometry file (*.M *.m)")
|
||||
self._dict["file_shp"] = _translate(
|
||||
"Geometry", "Shapefile (*.SHP *.shp)")
|
||||
self._dict["file_all"] = _translate("Geometry", "All files (*)")
|
||||
|
||||
self._dict["cross_section"] = _translate("Geometry", "cross-section")
|
||||
self._dict["cross_sections"] = _translate("Geometry", "cross-sections")
|
||||
@@ -64,9 +66,12 @@ class GeometryTranslate(MainTranslate):
|
||||
}
|
||||
|
||||
self._sub_dict["lm_dict"] = {
|
||||
"1": _translate("Geometry", "First guideline"),
|
||||
"2": _translate("Geometry", "Second guideline"),
|
||||
"3": _translate("Geometry", "Means between the two guideline"),
|
||||
"1": _translate("Geometry", "the first guide-line"),
|
||||
"2": _translate("Geometry", "the second guide-line"),
|
||||
"3": _translate(
|
||||
"Geometry",
|
||||
"the mean over the two guide-lines"
|
||||
),
|
||||
}
|
||||
|
||||
self._sub_dict["r_lm_dict"] = {}
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
import logging
|
||||
|
||||
from copy import deepcopy
|
||||
from tools import trace, timer
|
||||
from tools import trace, timer, logger_exception
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QMessageBox, QUndoCommand, QUndoStack,
|
||||
)
|
||||
|
||||
from Model.Geometry import Reach
|
||||
from Model.Except import exception_message_box
|
||||
|
||||
from Meshing.Mage import MeshingWithMage
|
||||
|
||||
@@ -209,20 +210,25 @@ class ImportCommand(QUndoCommand):
|
||||
|
||||
def redo(self):
|
||||
if self._profiles is None:
|
||||
self._profiles = self._reach.import_geometry(self._filename)
|
||||
self._profiles.reverse()
|
||||
try:
|
||||
self._profiles = self._reach.import_geometry(self._filename)
|
||||
self._profiles.reverse()
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
exception_message_box(e)
|
||||
else:
|
||||
for profile in self._profiles:
|
||||
self._reach.insert_profile(self._row, profile)
|
||||
|
||||
|
||||
class MeshingCommand(QUndoCommand):
|
||||
def __init__(self, reach, mesher, data):
|
||||
def __init__(self, reach, mesher, data, command):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._reach = reach
|
||||
self._data = data
|
||||
self._mesher = mesher
|
||||
self._command = command
|
||||
|
||||
self._profiles = reach.profiles.copy()
|
||||
self._profiles.reverse()
|
||||
@@ -237,10 +243,16 @@ class MeshingCommand(QUndoCommand):
|
||||
|
||||
def redo(self):
|
||||
if self._new_profiles is None:
|
||||
self._mesher.meshing(
|
||||
self._reach,
|
||||
**self._data
|
||||
)
|
||||
if self._command == "update_kp":
|
||||
self._mesher.update_kp(
|
||||
self._reach,
|
||||
**self._data
|
||||
)
|
||||
else:
|
||||
self._mesher.meshing(
|
||||
self._reach,
|
||||
**self._data
|
||||
)
|
||||
|
||||
self._new_profiles = self._reach.profiles.copy()
|
||||
self._new_profiles.reverse()
|
||||
@@ -249,3 +261,23 @@ class MeshingCommand(QUndoCommand):
|
||||
|
||||
for profile in self._new_profiles:
|
||||
self._reach.insert_profile(0, profile)
|
||||
|
||||
|
||||
class PurgeCommand(QUndoCommand):
|
||||
def __init__(self, reach, np_purge):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._reach = reach
|
||||
self._np_purge = np_purge
|
||||
|
||||
self._old = []
|
||||
for profile in self._reach.profiles:
|
||||
self._old.append(profile.points.copy())
|
||||
|
||||
def undo(self):
|
||||
for i in range(self._reach.number_profiles):
|
||||
self._reach.profiles[i]._points = self._old[i].copy()
|
||||
|
||||
def redo(self):
|
||||
for profile in self._reach._profiles:
|
||||
profile.purge(self._np_purge)
|
||||
@@ -0,0 +1,145 @@
|
||||
# UpdateKPDialog.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.QtGui import (
|
||||
QKeySequence,
|
||||
)
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QVariant, QAbstractTableModel,
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialogButtonBox, QComboBox, QUndoStack, QShortcut,
|
||||
QDoubleSpinBox,
|
||||
)
|
||||
|
||||
|
||||
class UpdateKPDialog(PamhyrDialog):
|
||||
_pamhyr_ui = "UpdateKPOptions"
|
||||
_pamhyr_name = "UpdateKP"
|
||||
|
||||
def __init__(self, reach, trad=None, parent=None):
|
||||
super(UpdateKPDialog, self).__init__(
|
||||
title=trad[self._pamhyr_name],
|
||||
trad=trad,
|
||||
options=[],
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._reach = reach
|
||||
self._profiles = None
|
||||
|
||||
self._init_default_values()
|
||||
|
||||
def _init_default_values(self):
|
||||
self._space_step = 50.0
|
||||
self._lplan = False
|
||||
self._lm = "3"
|
||||
self._linear = False
|
||||
self._begin_cs = -1
|
||||
self._end_cs = -1
|
||||
self._begin_dir = "un"
|
||||
self._end_dir = "np"
|
||||
self._origin = self._reach.profile(0)
|
||||
self._origin_value = self._reach.profile(0).kp
|
||||
|
||||
self._init_default_values_profiles()
|
||||
self._init_default_values_guidelines()
|
||||
|
||||
def _init_default_values_profiles(self):
|
||||
profiles = self.profiles
|
||||
|
||||
self.combobox_add_items("comboBox_origin", profiles)
|
||||
|
||||
self.find(QComboBox, "comboBox_origin").currentIndexChanged.connect(
|
||||
self.changed_profile)
|
||||
|
||||
def changed_profile(self):
|
||||
origin = self.get_combobox_text("comboBox_origin")
|
||||
self.set_double_spin_box(
|
||||
"doubleSpinBox_origin",
|
||||
self._reach.profile(self.profiles.index(origin)).kp
|
||||
)
|
||||
|
||||
@property
|
||||
def profiles(self):
|
||||
if self._profiles is None:
|
||||
self._profiles = list(
|
||||
map(
|
||||
lambda p: self._profile_name(p),
|
||||
self._reach.profiles
|
||||
)
|
||||
)
|
||||
|
||||
return self._profiles
|
||||
|
||||
def _profile_name(self, profile):
|
||||
name = profile.name
|
||||
|
||||
if name == "":
|
||||
name = f"{profile.kp}"
|
||||
else:
|
||||
name += f" ({profile.kp})"
|
||||
|
||||
return name
|
||||
|
||||
def _init_default_values_guidelines(self):
|
||||
gl, _ = self._reach.compute_guidelines()
|
||||
gl = list(gl)
|
||||
|
||||
bgl = ['un'] + gl + ['np']
|
||||
egl = ['un'] + gl + ['np']
|
||||
|
||||
self.combobox_add_items("comboBox_begin_gl", bgl)
|
||||
self.combobox_add_items("comboBox_end_gl", egl)
|
||||
|
||||
self.set_combobox_text("comboBox_begin_gl", self._begin_dir)
|
||||
self.set_combobox_text("comboBox_end_gl", self._end_dir)
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
return self._origin
|
||||
|
||||
@property
|
||||
def origin_value(self):
|
||||
return self._origin_value
|
||||
|
||||
@property
|
||||
def begin_dir(self):
|
||||
return self._begin_dir
|
||||
|
||||
@property
|
||||
def end_dir(self):
|
||||
return self._end_dir
|
||||
|
||||
def accept(self):
|
||||
origin = self.get_combobox_text("comboBox_origin")
|
||||
self._origin = self.profiles.index(origin)
|
||||
|
||||
self._origin_value = self.get_double_spin_box("doubleSpinBox_origin")
|
||||
|
||||
self._begin_dir = self.get_combobox_text("comboBox_begin_gl")
|
||||
self._end_dir = self.get_combobox_text("comboBox_end_gl")
|
||||
|
||||
super().accept()
|
||||
|
||||
def reject(self):
|
||||
self.close()
|
||||
+71
-13
@@ -56,6 +56,8 @@ from View.Geometry.PlotXY import PlotXY
|
||||
from View.Geometry.PlotAC import PlotAC
|
||||
from View.Geometry.PlotKPZ import PlotKPZ
|
||||
from View.Geometry.MeshingDialog import MeshingDialog
|
||||
from View.Geometry.UpdateKPDialog import UpdateKPDialog
|
||||
from View.Geometry.PurgeDialog import PurgeDialog
|
||||
from View.Geometry.Translate import GeometryTranslate
|
||||
from View.Geometry.Profile.Window import ProfileWindow
|
||||
|
||||
@@ -156,6 +158,9 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
def _compute_status_label(self):
|
||||
row = self.index_selected_row()
|
||||
if row is None:
|
||||
return
|
||||
|
||||
profile = self._reach.profile(row)
|
||||
|
||||
name = profile.name + " " + str(profile.kp)
|
||||
@@ -188,6 +193,8 @@ class GeometryWindow(PamhyrWindow):
|
||||
"action_delete": self.delete,
|
||||
"action_edit": self.edit_profile,
|
||||
"action_meshing": self.edit_meshing,
|
||||
"action_update_kp": self.update_kp,
|
||||
"action_purge": self.purge,
|
||||
}
|
||||
|
||||
for action in actions:
|
||||
@@ -227,7 +234,7 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._propagate_update(key=Modules.GEOMETRY)
|
||||
|
||||
def _propagated_update(self, key=Modules(0)):
|
||||
if Modules.NETWORK not in key:
|
||||
if Modules.NETWORK not in key and Modules.GEOMETRY not in key:
|
||||
return
|
||||
|
||||
self._update(propagate=False)
|
||||
@@ -241,6 +248,7 @@ class GeometryWindow(PamhyrWindow):
|
||||
file_types = [
|
||||
self._trad["file_st"],
|
||||
self._trad["file_m"],
|
||||
self._trad["file_shp"],
|
||||
self._trad["file_all"],
|
||||
]
|
||||
|
||||
@@ -296,7 +304,6 @@ class GeometryWindow(PamhyrWindow):
|
||||
"limites": [dlg.begin_cs, dlg.end_cs],
|
||||
"directrices": [dlg.begin_dir, dlg.end_dir],
|
||||
"lplan": dlg.lplan,
|
||||
"lm": dlg.lm,
|
||||
"linear": dlg.linear,
|
||||
}
|
||||
self._edit_meshing(data)
|
||||
@@ -317,10 +324,36 @@ class GeometryWindow(PamhyrWindow):
|
||||
src_except=e
|
||||
)
|
||||
|
||||
pyqtSlot(bool)
|
||||
def update_kp(self):
|
||||
try:
|
||||
dlg = UpdateKPDialog(
|
||||
reach=self._reach,
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
if dlg.exec():
|
||||
data = {
|
||||
"origin": dlg.origin,
|
||||
"directrices": [dlg.begin_dir, dlg.end_dir],
|
||||
"origin_value": dlg.origin_value,
|
||||
}
|
||||
self._update_kp(data)
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return
|
||||
|
||||
def changed_profile_slot(self, status):
|
||||
self.update_view1 = status
|
||||
def _update_kp(self, data):
|
||||
try:
|
||||
mesher = MeshingWithMageMailleurTT()
|
||||
self._table.update_kp(mesher, data)
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
raise ExternFileMissingError(
|
||||
module="mage",
|
||||
filename="MailleurTT",
|
||||
path=MeshingWithMageMailleurTT._path(),
|
||||
src_except=e
|
||||
)
|
||||
|
||||
def plot_xy(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
@@ -345,6 +378,7 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
self._plot_kpc = PlotKPZ(
|
||||
canvas=self._canvas_kpc,
|
||||
study=self._study,
|
||||
data=self._reach,
|
||||
trad=self._trad,
|
||||
toolbar=self._toolbar_kpc
|
||||
@@ -373,7 +407,6 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
def update_plot_ac(self, ind: int):
|
||||
self.tableView.model().blockSignals(True)
|
||||
self._plot_ac.current = ind
|
||||
self._plot_ac.update()
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
@@ -417,10 +450,9 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
def select_current_profile(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
row = self.index_selected_row()
|
||||
|
||||
if len(self.tableView.selectedIndexes()) > 0:
|
||||
row = self.index_selected_row()
|
||||
|
||||
if row is not None:
|
||||
self.find(QSlider, "verticalSlider").setValue(row)
|
||||
self.select_plot_xy(row)
|
||||
self.select_plot_kpc(row)
|
||||
@@ -479,10 +511,14 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._table.delete(rows)
|
||||
|
||||
def index_selected_row(self):
|
||||
return self.tableView\
|
||||
.selectionModel()\
|
||||
.selectedRows()[0]\
|
||||
.row()
|
||||
r = self.tableView\
|
||||
.selectionModel()\
|
||||
.selectedRows()
|
||||
|
||||
if len(r) > 0:
|
||||
return r[0].row()
|
||||
else:
|
||||
return None
|
||||
|
||||
def sort_ascending(self):
|
||||
self._table.sort_profiles(False)
|
||||
@@ -497,14 +533,36 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
def move_up(self):
|
||||
row = self.index_selected_row()
|
||||
if row is None:
|
||||
return
|
||||
|
||||
self._table.move_up(row)
|
||||
self.select_current_profile()
|
||||
|
||||
def move_down(self):
|
||||
row = self.index_selected_row()
|
||||
if row is None:
|
||||
return
|
||||
|
||||
self._table.move_down(row)
|
||||
self.select_current_profile()
|
||||
|
||||
def purge(self):
|
||||
self._table.purge()
|
||||
self.update_redraw()
|
||||
|
||||
def purge(self):
|
||||
try:
|
||||
dlg = PurgeDialog(
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
if dlg.exec():
|
||||
self._table.purge(dlg.np_purge)
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return
|
||||
|
||||
def duplicate(self):
|
||||
rows = [
|
||||
row.row() for row in
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# __init__.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 -*-
|
||||
@@ -28,7 +28,7 @@ class BasicHydraulicStructuresTranslate(MainTranslate):
|
||||
super(BasicHydraulicStructuresTranslate, self).__init__()
|
||||
|
||||
self._dict["Basic Hydraulic Structures"] = _translate(
|
||||
"BasicHydraulicStructures", "Basic Hydraulic Structures"
|
||||
"BasicHydraulicStructures", "Basic Hydraulic Structure"
|
||||
)
|
||||
|
||||
self._dict['msg_type_change_title'] = _translate(
|
||||
@@ -138,7 +138,7 @@ hydraulic structure values?"
|
||||
"BasicHydraulicStructures", "Check valve"
|
||||
),
|
||||
"UD": _translate(
|
||||
"BasicHydraulicStructures", "User defined"
|
||||
"BasicHydraulicStructures", "User-defined"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 323 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user