Compare commits

..
Author SHA1 Message Date
Theophile Terraz 22771c7f3a debug + pep8 2025-11-06 16:40:12 +01:00
Theophile Terraz 6d60a2017c add sound option in results for Q 2025-11-06 16:28:59 +01:00
Theophile Terraz 02db91f3d7 debug 2025-11-06 15:13:20 +01:00
Theophile Terraz d934b5e381 add waiting dialog while meshing 2025-11-04 09:30:40 +01:00
Theophile Terraz 279fa0bf5e debug get_water_limits 2025-10-31 11:01:43 +01:00
parouby abe24611b3 Merge branch 'scenario-dev-pa' into scenarios 2025-10-31 10:44:04 +01:00
parouby 17a18dde18 Results: Fix compare dialog and remove dead code. 2025-10-31 10:40:46 +01:00
parouby 044818072e Scenario: Fix scenario reload. 2025-10-31 10:27:10 +01:00
Theophile Terraz 8bd791b2c4 Merge branch 'scenarios' of gitlab.com:pamhyr/pamhyr2 into scenarios 2025-10-31 09:38:15 +01:00
Theophile Terraz c8d0d0c212 fix typo 2025-10-31 09:37:56 +01:00
parouby d31be61af8 Merge branch 'scenario-dev-pa' into scenarios 2025-10-31 09:14:50 +01:00
Theophile Terraz a8a408b5d5 fix RubarBE friction export + extrapolation 2025-10-30 16:16:35 +01:00
parouby ff9d25412c Results: Fix results_data delete. 2025-10-30 16:14:41 +01:00
parouby de7f3e63b1 BC: Fix BC undo command. 2025-10-30 09:57:05 +01:00
Theophile Terraz 879c151524 pep8 2025-10-29 17:13:09 +01:00
Theophile Terraz 7990a60d8b take care of frictions in update_rk 2025-10-29 17:09:42 +01:00
Theophile Terraz 8aa0e25da2 pep8 2025-10-28 17:21:10 +01:00
parouby 1e6a578890 Results: Minor fix. 2025-10-28 17:01:37 +01:00
Theophile Terraz 1dca9b98df debug rubarBE 2025-10-28 15:56:53 +01:00
parouby de180b3635 Pamhyr2: Fix pep8. 2025-10-28 11:12:45 +01:00
parouby 5ca763ac8c Results: Disable simple compare between solvers to solvers/scenarios. 2025-10-28 11:09:48 +01:00
parouby 188486f33a Results: Compare scenarios. 2025-10-27 17:14:29 +01:00
parouby bc08d06bb1 Study: Add copy method to prepare scenario comparison. 2025-10-27 14:50:08 +01:00
parouby 05e039011a Scenario: Fix workdir path for last result opening. 2025-10-27 09:29:33 +01:00
Theophile Terraz 7640123299 merge 2025-10-24 16:41:34 +02:00
Theophile Terraz e6dd983f97 more work on compare results 2025-10-24 16:36:06 +02:00
parouby 002ebc5928 Solver: Mage: Fix path name with whitespaces. 2025-10-24 15:18:31 +02:00
Theophile Terraz 84486c03ed more work on import data results 2025-10-24 15:11:11 +02:00
parouby dc7dfb2414 Results: Compare: Minor change. 2025-10-24 15:02:53 +02:00
parouby ee01beb844 Results: Compare: Simplify compare method and add message box on error. 2025-10-24 11:23:43 +02:00
Theophile Terraz 68b62a5630 debug water limits 2025-10-23 15:30:41 +02:00
Theophile Terraz 990d44dd07 again 2025-10-23 14:50:06 +02:00
parouby 819ae41c00 Results: Add posibility to save multiple results for different solver. 2025-10-23 11:23:44 +02:00
Theophile Terraz bf95b7f7e6 work on import data in result window 2025-10-22 16:22:59 +02:00
Theophile Terraz 861b78b928 debug read rubar3 results 2025-10-22 15:01:10 +02:00
parouby 08c414a66e Network: Resize network zone. 2025-10-21 17:01:39 +02:00
Theophile Terraz 57a80d00aa work on import data in results 2025-10-21 15:01:16 +02:00
parouby 70eae5f7d2 Results: Try add explicite import for rasterio. 2025-10-21 14:23:40 +02:00
parouby e2a65e2cdd git: CI: Use HIDDEN_IMPORTS variable and try collect-submodules. 2025-10-21 11:04:22 +02:00
parouby f03b74614e git: CI: Add hiddenimport on windows pyinstaller command. 2025-10-21 10:11:22 +02:00
parouby 532b3fd7fd Results: Fix pep8 comment. 2025-10-21 09:51:47 +02:00
parouby 64df4129d4 Results: Reactivate geotiff if package is imported. 2025-10-21 09:50:40 +02:00
parouby f6e35e2045 git: CI: Try add rasterio.sample into hiddenimport. 2025-10-21 09:23:47 +02:00
parouby a97717a624 Pamhyr: Window: Fix title without study opened. 2025-10-20 18:48:40 +02:00
parouby 3f650de52f git: CI: Add rasterio at hidden import. 2025-10-20 17:51:59 +02:00
parouby 1f4bda7976 Results: Minor change. 2025-10-20 16:48:49 +02:00
parouby a4423e45f8 Git: Update guix manifest. 2025-10-20 15:23:25 +02:00
parouby 1386c592bf Geotiff: Try load geotiff otherelse disable features. 2025-10-20 15:18:20 +02:00
32 changed files with 1415 additions and 629 deletions

No files matched your search

+9 -2
View File
@@ -24,6 +24,13 @@ stages:
- antivirus
- release
#########################
# PYINSTALLER VARIABLES #
#########################
variables:
HIDDEN_IMPORTS: "--hiddenimport rasterio --collect-submodules rasterio"
#############
# DOWNLOADS #
#############
@@ -292,7 +299,7 @@ build-linux:
- pip3 install -r ../requirements.txt
- pip3 install -U -r ../requirements.txt
# Run Pyinstaller
- pyinstaller -y --paths linux-venv/lib/python3.8/site-packages ../src/pamhyr.py
- pyinstaller -y $HIDDEN_IMPORTS --paths linux-venv/lib/python3.8/site-packages ../src/pamhyr.py
# Create directory
- mkdir -p pamhyr
- mkdir -p pamhyr/_internal
@@ -395,7 +402,7 @@ build-windows:
- python -m pip install -U -r ..\requirements.txt
# Run Pyinstaller
# - pyinstaller --noconsole -y ..\src\pamhyr.py
- pyinstaller -i ../src/View/ui/ressources/icon.ico --hide-console hide-early -y ..\src\pamhyr.py
- pyinstaller -i ../src/View/ui/ressources/icon.ico $HIDDEN_IMPORTS --hide-console hide-early -y ..\src\pamhyr.py
# Create directory
- mkdir pamhyr
- dir
+2 -1
View File
@@ -19,4 +19,5 @@
"python-pyqt@5" "python-pyqt5-sip"
"python-pyqtgraph" "python-qscintilla"
"python-numpy@1" "python-shapely" "python-pyshp"
"python-matplotlib" "python-rasterio"))
"python-matplotlib" "python-colorama"
"python-rasterio"))
+23 -8
View File
@@ -17,7 +17,7 @@
# -*- coding: utf-8 -*-
import logging
from numpy import sign
from numpy import sign, interp
from copy import deepcopy
from tools import logger_color_red, logger_color_reset, logger_exception
@@ -273,7 +273,7 @@ class InternalMeshing(AMeshingTool):
sect2.point(start2+len2).name = tag2
sect2.modified()
def update_rk(self, reach,
def update_rk(self, reach, begin_rk, end_rk,
step: float = 50,
limites=[-1, -1],
origin=0,
@@ -297,15 +297,25 @@ class InternalMeshing(AMeshingTool):
# 1: up -> downstream
# 2: down -> upstream
# else: keep current orientation
old_orientation = sign(reach.profiles[-1].rk - reach.profiles[0].rk)
if orientation == 1:
sgn = 1.0
elif orientation == 2:
sgn = -1.0
else:
sgn = sign(reach.profiles[-1].rk - reach.profiles[0].rk)
sgn = old_orientation
rk = [0.0]*nprof
rk[origin] = origin_value
new_rk = [0.0]*nprof
new_rk[origin] = origin_value
frictions = reach._parent.frictions
old_begin_rk = list(map(lambda x: x * old_orientation,
begin_rk))
old_end_rk = list(map(lambda x: x * old_orientation,
end_rk))
old_rk = list(map(lambda x: x * old_orientation,
reach.get_rk()))
if origin < nprof - 1:
for i in range(origin+1, nprof):
@@ -316,7 +326,7 @@ class InternalMeshing(AMeshingTool):
d2 = reach.profiles[i-1].named_point(
directrices[1]
).dist_2d(reach.profiles[i].named_point(directrices[1]))
rk[i] = rk[i-1] + (sgn * (d1 + d2) / 2)
new_rk[i] = new_rk[i-1] + (sgn * (d1 + d2) / 2)
if origin > 0:
for i in reversed(range(0, origin)):
# 2D
@@ -326,6 +336,11 @@ class InternalMeshing(AMeshingTool):
d2 = reach.profiles[i+1].named_point(
directrices[1]
).dist_2d(reach.profiles[i].named_point(directrices[1]))
rk[i] = rk[i+1] - (sgn * (d1 + d2) / 2)
new_rk[i] = new_rk[i+1] - (sgn * (d1 + d2) / 2)
return rk
new_begin_rk = interp(old_begin_rk, old_rk, new_rk)
new_end_rk = interp(old_end_rk, old_rk, new_rk)
if abs(sgn - old_orientation) > 0.1: # orientation changed
return new_rk, new_end_rk, new_begin_rk
else:
return new_rk, new_begin_rk, new_end_rk
@@ -582,10 +582,10 @@ class BoundaryCondition(SQLSubModel):
self._data.index(bc)
def get_i(self, index):
if len(self.data) == 0:
if len(self._data) == 0:
return None
return self.data[index]
return self._data[index]
def get_range(self, _range):
lst = []
+26 -34
View File
@@ -749,8 +749,8 @@ class ProfileXYZ(Profile, SQLSubModel):
if zz[i] >= z and zz[i+1] < z:
y = np.interp(
z,
[zz[i], zz[i+1]],
[station[i], station[i+1]]
[zz[i+1], zz[i]],
[station[i+1], station[i]]
)
line.append([y, z])
@@ -839,11 +839,8 @@ class ProfileXYZ(Profile, SQLSubModel):
for i in range(self.number_points-1):
if zz[i] > z and zz[i+1] <= z:
y = np.interp(
z,
[zz[i], zz[i+1]],
[station[i], station[i+1]]
)
fact = (z - zz[i]) / (zz[i+1] - zz[i])
y = station[i] + fact * (station[i+1] - station[i])
start.append(y)
end = []
@@ -852,11 +849,8 @@ class ProfileXYZ(Profile, SQLSubModel):
for i in reversed(range(self.number_points-1)):
if zz[i] <= z and zz[i+1] > z:
y = np.interp(
z,
[zz[i], zz[i+1]],
[station[i], station[i+1]]
)
fact = (z - zz[i]) / (zz[i+1] - zz[i])
y = station[i] + fact * (station[i+1] - station[i])
end.append(y)
if len(start) != len(end):
@@ -887,33 +881,31 @@ class ProfileXYZ(Profile, SQLSubModel):
# Interpolate points at river left side
if (i_left > 0):
x = np.interp(
z,
[self.point(i_left).z, self.point(i_left - 1).z],
[self.point(i_left).x, self.point(i_left - 1).x]
)
y = np.interp(
z,
[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, name="wl_left")
if abs(self.point(i_left - 1).z - self.point(i_left).z) < 1e-20:
pt_left = self.point(i_left)
else:
fact = (z - self.point(i_left).z) / \
(self.point(i_left - 1).z - self.point(i_left).z)
x = self.point(i_left).x + fact * \
(self.point(i_left - 1).x - self.point(i_left).x)
y = self.point(i_left).y + fact * \
(self.point(i_left - 1).y - self.point(i_left).y)
pt_left = PointXYZ(x=x, y=y, z=z, name="wl_left")
else:
pt_left = self.point(0)
# Interpolate points at river right side
if (i_right < self.number_points - 1):
x = np.interp(
z,
[self.point(i_right).z, self.point(i_right + 1).z],
[self.point(i_right).x, self.point(i_right + 1).x]
)
y = np.interp(
z,
[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, name="wl_right")
if abs(self.point(i_right + 1).z - self.point(i_right).z) < 1e-20:
pt_right = self.point(i_right)
else:
fact = (z - self.point(i_right).z) / \
(self.point(i_right + 1).z - self.point(i_right).z)
x = self.point(i_right).x + fact * \
(self.point(i_right + 1).x - self.point(i_right).x)
y = self.point(i_right).y + fact * \
(self.point(i_right + 1).y - self.point(i_right).y)
pt_right = PointXYZ(x=x, y=y, z=z, name="wl_right")
else:
pt_right = self.point(self.number_points - 1)
+31 -13
View File
@@ -52,6 +52,9 @@ class Results(SQLSubModel):
"study_revision": study.status.version,
}
if solver is not None:
self.set("solver_type", solver._type)
@property
def date(self):
date = self._meta_data["creation_date"]
@@ -112,7 +115,10 @@ class Results(SQLSubModel):
)
""")
return True
if ext != "":
return True
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
@@ -147,7 +153,7 @@ class Results(SQLSubModel):
)
if table is None:
yield new
yield
if len(table) > 1:
logger.warning("Multiple results for this scenario")
@@ -178,23 +184,33 @@ class Results(SQLSubModel):
data["timestamps"] = sorted(ts)
new_results._river = River._db_load(execute, data)
new = new_results
yield (solver_type, new_results)
yield new
def _db_save_clear(self, execute, solver_type, data=None):
old_pid = execute(
"SELECT pamhyr_id FROM results " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND solver_type = '{solver_type}'"
)
if len(old_pid) != 0:
for pid in old_pid:
pid = pid[0]
execute(
"DELETE FROM results " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND solver_type = '{solver_type}'"
)
execute(
"DELETE FROM results_data " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}"
)
def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario:
return
execute(
"DELETE FROM results " +
f"WHERE scenario = {self._owner_scenario}"
)
execute(
"DELETE FROM results_data " +
f"WHERE scenario = {self._owner_scenario}"
)
pid = self._pamhyr_id
if self._solver is None:
solver_name = self.get("solver_name")
@@ -203,6 +219,8 @@ class Results(SQLSubModel):
solver_name = self._solver._name
solver_type = self._solver._type
self._db_save_clear(execute, solver_type, data=data)
ts = sorted(self.get("timestamps"))
sf = ">" + ''.join(itertools.repeat("d", len(ts)))
+1 -1
View File
@@ -227,7 +227,7 @@ class Profile(SQLSubModel):
values = list(map(float, values))
sf = ">" + ''.join(itertools.repeat("f", len(values)))
len_values = len(values)
elif key is "sl":
elif key == "sl":
# HACK: Some dirty code to transforme list of list of
# tuple to list of values and ensure the values is
# float type...
+10 -5
View File
@@ -505,7 +505,7 @@ class River(Graph):
self._D90AdisTS = D90AdisTSList(status=self._status)
self._DIFAdisTS = DIFAdisTSList(status=self._status)
self._results = None
self._results = {}
@classmethod
def _db_create(cls, execute):
@@ -620,7 +620,10 @@ class River(Graph):
return new
def _db_load_results(self, execute, data=None):
self._results = Results._db_load(execute, data)
results_lst = Results._db_load(execute, data)
for solv_type, results in results_lst:
self._results[solv_type] = results
def _db_save(self, execute, data=None):
self._db_save_delete_artefact(execute, data)
@@ -647,8 +650,8 @@ class River(Graph):
objs.append(self._D90AdisTS)
objs.append(self._DIFAdisTS)
if self.results is not None:
objs.append(self.results)
for solv_type in self.results:
objs.append(self.results[solv_type])
self._save_submodel(execute, objs, data)
return True
@@ -902,7 +905,9 @@ Last export at: @date."""
@results.setter
def results(self, results):
self._results = results
solv_type = results.get("solver_type")
self._results[solv_type] = results
def _split_reach(self, reach, profile):
node1 = reach.node1
+1 -1
View File
@@ -276,7 +276,7 @@ class Scenario(SQLSubModel):
if self.id != 0:
srep = os.path.join(
self.parent.workdir(),
"senario_" + str(self.id)
"scenario_" + str(self.id)
)
return srep
+81 -25
View File
@@ -44,7 +44,10 @@ class Study(SQLModel):
River,
]
def __init__(self, filename=None, init_new=True):
def __init__(self, filename=None, init_new=True, copy=False):
if copy:
return
# Metadata
self.creation_date = datetime.now()
self.last_modification_date = datetime.now()
@@ -476,31 +479,10 @@ class Study(SQLModel):
return new
def reload_from_scenario(self, scenario):
if scenario in self._river_scenario_cache:
self._river = self._river_scenario_cache[scenario]
self.status.scenario = scenario
else:
def sql_exec(sql):
return self.execute(
sql, fetch_one=False, commit=True
)
river = self.load_scenario(scenario)
self._river = river
self.status.scenario = scenario
data = {
"status": self.status,
"loaded_pid": set(),
"scenario": scenario
}
# Reload river data
river = River._db_load(
sql_exec, data=data
)
data["study"] = self
river._db_load_results(sql_exec, data=data)
self._river_scenario_cache[scenario] = river
self._river = river
self.status.scenario = scenario
if reduce(
lambda a, s: a or (s.parent is scenario),
@@ -511,6 +493,80 @@ class Study(SQLModel):
else:
self.status.set_as_editable()
def load_scenario(self, scenario):
if scenario in self._river_scenario_cache:
return self._river_scenario_cache[scenario]
def sql_exec(sql):
return self.execute(
sql, fetch_one=False, commit=True
)
old_scenario = self.status.scenario
self.status.scenario = scenario
data = {
"status": self.status,
"loaded_pid": set(),
"scenario": scenario
}
# Load river data
river = River._db_load(
sql_exec, data=data
)
data["study"] = self
river._db_load_results(sql_exec, data=data)
self._river_scenario_cache[scenario] = river
self.status.scenario = old_scenario
return river
def copy_from_scenario(self, scenario):
new = self._copy()
new._river = self.load_scenario(scenario)
def set_status(obj):
obj._status = new.status
new.river._data_traversal(
modifier=lambda obj, data: set_status(obj),
)
return new
def _copy(self):
"""
This method make a copy of current study. This copy is like an
empty shell, it's not fully functional. Study object use
SQLite connection to file, this copy as no valid connection.
(!) Please use this copy as read only object!
"""
new = Study(copy=True)
new._filename = ""
new.creation_date = self.creation_date
new.last_modification_date = self.last_modification_date
new.last_save_date = self.last_save_date
new._name = self._name
new.description = self.description
new._time_system = self._time_system
new._date = self._date
new.status = StudyStatus()
new.scenarios = self.scenarios
new.status.scenario = self.status.scenario
new._river = self._river
new._river_scenario_cache = self._river_scenario_cache
return new
def duplicate_current_scenario(self):
source = self.status.scenario_id
new = self.scenarios.new(
+6 -4
View File
@@ -177,15 +177,15 @@ class Mage(CommandLineSolver):
return lst
def input_param(self):
name = self._study.name
name = self._study.name.replace(" ", "_")
return f"{name}.REP"
def output_param(self):
name = self._study.name
name = self._study.name.replace(" ", "_")
return f"{name}.BIN"
def log_file(self):
name = self._study.name
name = self._study.name.replace(" ", "_")
return f"{name}.TRA"
def _export_REP_additional_lines(self, study, rep_file):
@@ -819,7 +819,8 @@ class Mage(CommandLineSolver):
repertory=repertory,
name=name,
)
fname = os.path.join(repertory, f"{name}.BIN")
fname = os.path.join(repertory, f"{name}.BIN".replace(" ", "_"))
if not os.path.isfile(fname):
logger.info(f"Result file {name}.BIN does not exist")
return None
@@ -1374,6 +1375,7 @@ class Mage8(Mage):
results = super(Mage8, self).results(study, repertory, qlog, name=name)
if results is None:
return None
if with_gra:
self.read_gra(study, repertory, results, qlog, name=name)
+95 -57
View File
@@ -52,8 +52,8 @@ class Rubar3(CommandLineSolver):
("rubarbe_condav", "3"),
("rubarbe_regime", "0"),
("rubarbe_iodev", "n"),
# ("rubarbe_iodebord", ""),
# ("rubarbe_iostockage", ""),
("rubarbe_iodebord", ""),
("rubarbe_iostockage", ""),
("rubarbe_iopdt", "y"),
("rubarbe_iovis", "n"),
("rubarbe_rep", "n"),
@@ -73,36 +73,37 @@ class Rubar3(CommandLineSolver):
("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"),
("rubarbe_trased", "n"),
("rubarbe_optfpc", "0"),
# trased parameters
("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"),
("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
@@ -169,7 +170,7 @@ class Rubar3(CommandLineSolver):
it = iter(params)
line = 0
while line < 25:
while line < 27:
param = next(it)
name = param.name
value = param.value
@@ -394,25 +395,59 @@ class Rubar3(CommandLineSolver):
lm = len(reach) + 1
f.write(f"{lm:>6}\n")
def get_stricklers_from_rk(rk):
return next(
map(
lambda s: (
s.begin_strickler.medium if version == "2"
else s.begin_strickler.minor
),
filter(
lambda f: rk in f,
edge.frictions.lst
)
lst = list(filter(
lambda f: f.is_full_defined(),
edge.frictions.frictions
))
rk_min = 9999999.9
rk_max = -9999999.9
coeff_min = -1.0
coeff_max = -1.0
for s in lst: # TODO optimise ?
if s.begin_rk > rk_max:
rk_max = s.begin_rk
coeff_max = s.begin_strickler
if s.begin_rk < rk_min:
rk_min = s.begin_rk
coeff_min = s.begin_strickler
if s.end_rk > rk_max:
rk_max = s.end_rk
coeff_max = s.end_strickler
if s.end_rk < rk_min:
rk_min = s.end_rk
coeff_min = s.end_strickler
def get_stricklers_from_rk(rk, lst):
coeff = None
if rk > rk_max:
coeff = coeff_max
elif rk < rk_min:
coeff = coeff_min
else:
for s in lst:
print(s.begin_rk, s.end_rk)
if (rk >= s.begin_rk and rk <= s.end_rk or
rk <= s.begin_rk and rk >= s.end_rk):
coeff = s.begin_strickler # TODO: inerpolate
break
# TODO interpolation if rk is not in frictons
if coeff is None:
logger.error(
"Study frictions are not fully defined"
)
)
return None
return coeff.medium if version == "2" else coeff.minor
ind = 1
for mail in edge.reach.inter_profiles_rk():
coef = get_stricklers_from_rk(mail)
f.write(f"{ind:>6} {coef:>12.5f}")
coef = get_stricklers_from_rk(mail, lst)
if coef is not None:
f.write(f"{ind:>6} {coef:>12.5f}")
ind += 1
f.write("\n")
@@ -583,27 +618,30 @@ class Rubar3(CommandLineSolver):
for reach, lm in reachs:
# First profile
e, s, q, z = read_data_line(f)
set_and_compute_limites(reach, 0, z, q, s)
h, s, q, z = read_data_line(f)
set_and_compute_limites(reach, 0, z+h, q, s)
# For each profile
ind = 1
pe, ps, pq, pz = read_data_line(f)
ph, ps, pq, pz = read_data_line(f)
while ind < lm - 2:
e, s, q, z = read_data_line(f)
h, s, q, z = read_data_line(f)
set_and_compute_limites(
reach, ind,
(pz + z) / 2,
(pq + q) / 2,
(ps + s) / 2
(ph + pz + ph + h) / 2,
(pz + s) / 2,
(pq + q) / 2
)
pe, ps, pq, pz = z, s, q, e
ph = h
ps = s
pq = q
pz = z
ind += 1
# Last profile
e, s, q, z = read_data_line(f)
set_and_compute_limites(reach, ind, z, q, s)
h, s, q, z = read_data_line(f)
set_and_compute_limites(reach, ind, z+h, q, s)
@timer
def results(self, study, repertory, qlog=None, name="0"):
+7 -3
View File
@@ -58,7 +58,7 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel):
def get_true_data_row(self, row):
bc = self._data.get_i(row)
bc = self._data.data[row]
return next(
map(
@@ -83,6 +83,7 @@ class TableModel(PamhyrTableModel):
value = QVariant()
if 0 <= column < 2:
row = self.get_true_data_row(row)
v = self._data.get_i(row)[column]
if self._data.get_type_column(column) == float:
if type(v) is str:
@@ -106,13 +107,16 @@ class TableModel(PamhyrTableModel):
column = index.column()
try:
row = self.get_true_data_row(row)
self._undo.push(
SetDataCommand(
self._data, row, column, value
self._data, row,
column, value
)
)
except Exception as e:
logger.info(e)
logger.warning(e)
logger.debug(traceback.format_exc())
self.update()
@@ -83,13 +83,13 @@ class AddCommand(QUndoCommand):
self._new = None
def undo(self):
self._data.delete_i([self._index])
self._new.set_as_deleted()
def redo(self):
if self._new is None:
self._new = self._data.add(self._index)
else:
self._data.insert(self._index, self._new)
self._new.set_as_not_deleted()
class DelCommand(QUndoCommand):
@@ -101,15 +101,16 @@ class DelCommand(QUndoCommand):
self._bc = []
for row in rows:
self._bc.append((row, self._data.get_i(row)))
self._bc.append(self._data.get_i(row))
self._bc.sort()
def undo(self):
for row, el in self._bc:
self._data.insert(row, el)
for el in self._bc:
el.set_as_not_deleted()
def redo(self):
self._data.delete_i(self._rows)
for el in self._bc:
el.set_as_deleted()
class SortCommand(QUndoCommand):
+22 -4
View File
@@ -237,22 +237,40 @@ class UpdateRKCommand(QUndoCommand):
self._data = data
self._mesher = mesher
self._rks = reach.get_rk()
self._frictions = [f for f in reach._parent.frictions.frictions
if f.is_full_defined()]
self._begin_rk = [f.begin_rk for f in self._frictions]
self._end_rk = [f.end_rk for f in self._frictions]
self._new_rks = None
self._new_begin_rk = None
self._new_end_rk = None
def undo(self):
for rk, profile in zip(self._rks, self._reach.profiles):
profile.rk = rk
for begin_rk, end_rk, friction in zip(self._begin_rk,
self._end_rk,
self._frictions):
friction.begin_rk = begin_rk
friction.end_rk = end_rk
def redo(self):
if self._new_rks is None:
self._new_rks = self._mesher.update_rk(
self._reach,
**self._data
)
self._new_rks, self._new_begin_rk, self._new_end_rk = \
self._mesher.update_rk(
self._reach, self._begin_rk, self._end_rk,
**self._data
)
for rk, profile in zip(self._new_rks, self._reach.profiles):
profile.rk = rk
for begin_rk, end_rk, friction in zip(self._new_begin_rk,
self._new_end_rk,
self._frictions):
friction.begin_rk = begin_rk
friction.end_rk = end_rk
class MeshingCommand(QUndoCommand):
+15 -1
View File
@@ -50,6 +50,8 @@ from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.SelectReach.Window import SelectReachWindow
from View.WaitingDialog import WaitingDialog
from Meshing.Internal import InternalMeshing
from View.Geometry.Table import GeometryReachTableModel
@@ -326,7 +328,19 @@ class GeometryWindow(PamhyrWindow):
def _edit_meshing(self, data):
try:
mesher = InternalMeshing()
self._table.meshing(mesher, data, self.tableView)
tableview = self.tableView
def fn():
self._table.meshing(mesher, data, tableview)
# self._table.meshing(mesher, data, tableview)
dlg2 = WaitingDialog(
payload_fn=fn,
title="waiting_mesh",
parent=self
)
dlg2.exec_()
self._table.update()
except Exception as e:
logger_exception(e)
+110 -81
View File
@@ -32,7 +32,7 @@ from platformdirs import user_cache_dir
from Solver.AdisTS import AdisTS
from Solver.Mage import Mage8
from Solver.RubarBE import Rubar3
from tools import logger_exception, pamhyr_db_need_update
from tools import logger_exception, pamhyr_db_need_update, timer
from PyQt5 import QtGui
from PyQt5.QtGui import (
@@ -84,10 +84,12 @@ from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import (
SelectSolverWindow,
SolverLogWindow,
CompareSolversWindow
)
from View.Results.Window import ResultsWindow
from View.Results.CompareDialog import (
CompareScenariosWindow
)
from View.RunSolver.WindowAdisTS import (
SelectSolverWindowAdisTS,
@@ -128,8 +130,8 @@ no_model_action = [
model_action = [
"action_menu_close", "action_menu_edit", "action_menu_save",
"action_menu_save_as", "action_toolBar_close", "action_toolBar_save",
"action_menu_numerical_parameter", "action_open_results_from_file",
"action_open_results_adists",
"action_menu_numerical_parameter", "action_menu_open_results_from_file",
"action_menu_open_results_adists",
]
other_model_action = [
@@ -150,8 +152,9 @@ define_model_action = [
"action_menu_run_solver", "action_menu_sediment_layers",
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs",
"action_menu_edit_hydraulic_structures", "action_menu_additional_file",
"action_menu_results_last", "action_open_results_from_file",
"action_compare_results", "action_menu_boundary_conditions_sediment",
"action_menu_results_last", "action_menu_open_results_from_file",
"action_menu_compare_scenarios_results",
"action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_rk",
"action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90", "action_menu_dif",
@@ -296,9 +299,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_rep_additional_lines": self.open_rep_lines,
"action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results,
"action_open_results_from_file": self.open_results_from_file,
"action_compare_results": self.compare_results,
"action_open_results_adists": self.open_results_adists,
"action_menu_open_results_from_file": self.open_results_from_file,
"action_menu_compare_scenarios_results":
self.compare_results_scenarios,
"action_menu_open_results_adists": self.open_results_adists,
# Help
"action_menu_pamhyr_users_wiki": self.open_doc_user,
"action_menu_pamhyr_developers_pdf":
@@ -596,7 +600,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if self._study is None:
return None
return self._study.results
results = self._study.results
if self._last_solver._type in results:
return self._study.results[self._last_solver._type]
return None
@last_results.setter
def last_results(self, results):
@@ -1507,7 +1515,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return False
def open_solver_results(self, solver, results=None):
logger.info(f"{solver} {results}")
logger.info(f"Open results for '{solver}' ({results})")
def reading_fn():
self._tmp_results = results
@@ -1637,12 +1645,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
res.show()
def _solver_workdir(self, solver):
def _solver_workdir(self, solver, scenario=None):
if scenario is None:
scenario = self._study.status.scenario
workdir = os.path.join(
os.path.dirname(self._study.filename),
"_PAMHYR_",
self._study.name.replace(" ", "_"),
solver.name.replace(" ", "_"),
scenario.workdir(),
)
return workdir
@@ -1697,32 +1709,36 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
results=file_name[0]
)
def compare_results(self):
def compare_results_scenarios(self):
if self._study is None:
return
run = CompareSolversWindow(
dlg = CompareScenariosWindow(
study=self._study,
config=self.conf,
parent=self
)
if run.exec():
results = self.diff_results(run.solver1,
run.solver2)
else:
if not dlg.exec():
return
results = self.diff_results(
dlg.solver1, dlg.solver2,
scenario1=dlg.scenario1,
scenario2=dlg.scenario2
)
# At least one result not available
if results is None:
return
# Windows already opened
if self.sub_window_exists(
ResultsWindow,
CompareScenariosWindow,
data=[self._study, None] +
[r._solver for r in results] +
[r._repertory for r in results] +
[r._name for r in results]
[r._name for r in results] +
[dlg.scenario1, dlg.scenario2]
):
return
@@ -1733,97 +1749,109 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
res.show()
def diff_results(self, solver1, solver2):
if solver1 is None:
# TODO message
return None
if solver2 is None:
# TODO message
def msg_diff_results_param(self):
self.message_box(
window_title=self._trad["Error"],
text=self._trad["mb_diff_results_title"],
informative_text=self._trad["mb_diff_results_param_msg"]
)
def msg_diff_results_compatibility(self):
self.message_box(
window_title=self._trad["Error"],
text=self._trad["mb_diff_results_title"],
informative_text=self._trad["mb_diff_results_compatibility_msg"]
)
@timer
def diff_results(self, solver1, solver2,
scenario1=None, scenario2=None):
if solver1 is None or solver2 is None:
self.msg_diff_results_param()
return None
solver3 = GenericSolver(solver1.name+" - "+solver2.name)
solver4 = GenericSolver(solver1.name)
solver5 = GenericSolver(solver2.name)
study1 = self._study
study2 = self._study
s3_name = solver1.name + " <> " + solver2.name
if not (scenario2 is None or scenario2 is None):
study1 = self._study.copy_from_scenario(scenario1)
study2 = self._study.copy_from_scenario(scenario2)
s3_name = scenario1.name + " <> " + scenario2.name
solver3 = GenericSolver(s3_name)
result1 = solver1.results(
self._study,
self._solver_workdir(solver1),
)
if result1 is None:
# TODO message
return None
study1,
self._solver_workdir(solver1, scenario=scenario1),
)
result2 = solver2.results(
self._study,
self._solver_workdir(solver2),
)
study2,
self._solver_workdir(solver2, scenario=scenario2),
)
if result2 is None:
# TODO message
if result1 is None or result2 is None:
self.msg_diff_results_param()
return None
if result2.get("nb_reach") != result1.get("nb_reach"):
# TODO message
if result1.get("nb_reach") != result2.get("nb_reach"):
self.msg_diff_results_compatibility()
return None
if result2.get("nb_profile") != result1.get("nb_profile"):
# TODO message
if result1.get("nb_profile") != result2.get("nb_profile"):
self.msg_diff_results_compatibility()
return None
# return [result1, result2]
return self._diff_results(
solver1, solver2, solver3,
result1, result2,
)
@timer
def _diff_results(self, solver1, solver2, solver3, result1, result2):
result3 = Results(study=self._study, solver=solver3)
ts = sorted(
list(
result1.get("timestamps").intersection(
result2.get("timestamps")
)
)
)
result3 = Results(self._study, solver3)
result4 = Results(self._study, solver1)
result5 = Results(self._study, solver2)
result3.set("nb_reach", result1.get("nb_reach"))
result4.set("nb_reach", result1.get("nb_reach"))
result5.set("nb_reach", result1.get("nb_reach"))
result3.set("nb_profile", result1.get("nb_profile"))
result4.set("nb_profile", result1.get("nb_profile"))
result5.set("nb_profile", result1.get("nb_profile"))
ts = sorted(list(result1.get("timestamps").intersection(
result2.get("timestamps"))))
result3.set("timestamps", ts)
result4.set("timestamps", ts)
result5.set("timestamps", ts)
for i in range(int(result1.get("nb_reach"))):
# Add reach to results reach list
r = result3.river.add(i)
r = result4.river.add(i)
r = result5.river.add(i)
for timestamp in result3.get("timestamps"):
for r in range(int(result1.get("nb_reach"))):
reach1 = result1.river.reach(r)
reach2 = result2.river.reach(r)
reach3 = result3.river.reach(r)
reach4 = result4.river.reach(r)
reach5 = result5.river.reach(r)
for p, (profile1, profile2) in enumerate(zip(
reach1.profiles, reach2.profiles)):
for profile1, profile2, profile3 in zip(
reach1.profiles,
reach2.profiles,
reach3.profiles):
for key in ["Z", "Q", "V"]:
d1 = profile1.get_ts_key(timestamp, key)
d2 = profile2.get_ts_key(timestamp, key)
d = d1-d2
reach3.set(p, timestamp, key, d)
reach4.set(p, timestamp, key, d1)
reach5.set(p, timestamp, key, d2)
limits = reach3.profile(p).geometry.get_water_limits(
reach3.profile(p).get_ts_key(timestamp, "Z")
)
reach3.set(
p, timestamp,
"water_limits",
limits
)
limits = profile1.get_ts_key(timestamp, "water_limits")
reach4.set(p, timestamp, "water_limits", limits)
limits = profile2.get_ts_key(timestamp, "water_limits")
reach5.set(p, timestamp, "water_limits", limits)
d = d1 - d2
return [result4, result5, result3]
profile3.set(timestamp, key, d)
limits = profile3.geometry\
.get_water_limits(
profile3.get_ts_key(timestamp, "Z")
)
profile3.set(timestamp, "water_limits", limits)
return [result1, result2, result3]
def open_results_adists(self):
if self._study is None:
@@ -1866,6 +1894,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
solver_results_adists = solver_results.results(
self._study,
repertory=dir_path, qlog=None) # self._output)
logger.info(f"Select results: {dir_path}")
if len(bin_list) >= 2 and ("total_sediment.bin" in bin_list):
self.open_solver_results_adists(
+2 -1
View File
@@ -481,7 +481,7 @@ class GraphWidget(QGraphicsView):
def setup_scene(self, min_size, max_size, size):
scene = QGraphicsScene(self)
scene.setItemIndexMethod(QGraphicsScene.NoIndex)
scene.setSceneRect(0, 0, 2000, 2000)
scene.setSceneRect(0, 0, 6000, 6000)
self.setScene(scene)
self.setCacheMode(QGraphicsView.CacheBackground)
@@ -492,6 +492,7 @@ class GraphWidget(QGraphicsView):
self.scale(1, 1)
self.previousScale = 1
self.centerOn(1000.0, 1000.0)
if min_size:
self.setMinimumSize(*min_size)
+149
View File
@@ -0,0 +1,149 @@
import os
import logging
import tempfile
from queue import Queue
from tools import trace, timer, logger_exception
from View.Tools.PamhyrWindow import PamhyrDialog, PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
)
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect, QTimer, QProcess,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QTextEdit,
)
from View.WaitingDialog import WaitingDialog
_translate = QCoreApplication.translate
logger = logging.getLogger()
class CompareScenariosWindow(PamhyrDialog):
_pamhyr_ui = "CompareScenarios"
_pamhyr_name = "Compare scenarios"
def __init__(self, study=None, config=None,
parent=None):
self._solvers = []
self._scenarios = []
name = _translate("Solver", "Compare solvers")
super(CompareScenariosWindow, self).__init__(
title=name,
study=study,
config=config,
options=[],
parent=parent
)
self.setup_solvers()
self.setup_scenarios()
self.setup_connections()
self.select_last_solver()
def setup_solvers(self):
solvers = self._config.solvers
solvers_name = list(
map(
self._format_solver_name, solvers
)
)
self.combobox_add_items("comboBoxSolver1", solvers_name)
self.combobox_add_items("comboBoxSolver2", solvers_name)
def setup_scenarios(self):
scenarios = self._study.scenarios.lst
scenarios_name = list(
map(
lambda s: s.name, scenarios
)
)
self.combobox_add_items("comboBoxScenario1", scenarios_name)
self.combobox_add_items("comboBoxScenario2", scenarios_name)
def setup_connections(self):
self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.reject)
def select_last_solver(self):
solvers = self._config.solvers
last = self._config.last_solver_name
solver = list(
filter(
lambda s: s.name == last,
solvers
)
)
if len(solver) != 0:
self.set_combobox_text(
"comboBoxSolver1",
self._format_solver_name(solver[0])
)
self.set_combobox_text(
"comboBoxSolver2",
self._format_solver_name(solver[0])
)
def _format_solver_name(self, solver):
return f"{solver.name} - ({solver._type})"
@property
def solver1(self):
return self._solvers[0]
@property
def solver2(self):
return self._solvers[1]
@property
def scenario1(self):
return self._scenarios[0]
@property
def scenario2(self):
return self._scenarios[1]
def accept(self):
for cb in ["comboBoxSolver1", "comboBoxSolver2"]:
name = self.get_combobox_text(cb)
name = name.rsplit(" - ", 1)[0]
self._solvers.append(
next(
filter(
lambda s: s.name == name,
self._config.solvers
)
)
)
for cb in ["comboBoxScenario1", "comboBoxScenario2"]:
name = self.get_combobox_text(cb)
self._scenarios.append(
next(
filter(
lambda s: s.name == name,
self._study.scenarios.lst
)
)
)
super(CompareScenariosWindow, self).accept()
+24 -14
View File
@@ -599,13 +599,7 @@ class CustomPlot(PamhyrPlot):
self.lines["wet_area"] = line
# Legend
lns = reduce(
lambda acc, line: acc + line,
map(lambda line: self.lines[line], self.lines),
[]
)
labs = list(map(lambda line: self._trad[line], self.lines))
self.canvas.axes.legend(lns, labs, loc="best")
self.update_legend()
def _redraw_rk(self):
results = self.data[self._current_res_id]
@@ -1103,13 +1097,7 @@ class CustomPlot(PamhyrPlot):
self._customize_x_axes_time(ts)
# Legend
lns = reduce(
lambda acc, line: acc + line,
map(lambda line: self.lines[line], self.lines),
[]
)
labs = list(map(lambda line: self._trad[line], self.lines))
self.canvas.axes.legend(lns, labs, loc="best")
self.update_legend()
def _redraw_time(self):
@@ -1419,3 +1407,25 @@ class CustomPlot(PamhyrPlot):
x = self._timestamp
self._current.set_data([x, x], self.canvas.axes.get_ylim())
self.canvas.draw_idle()
def update_legend(self):
lns = reduce(
lambda acc, line: acc + line,
map(lambda line: self.lines[line], self.lines),
[]
)
labs = list(map(lambda line: self._trad[line], self.lines))
self.canvas.axes.legend(lns, labs, loc="best")
def add_imported_plot(self, data):
if (data["type_x"] == self._x and
data["type_y"] in self._y):
while data["legend"] in self.lines:
data["legend"] += '*'
self._trad._dict[data["legend"]] = data["legend"] + data["unit"]
self.lines[data["legend"]] = self.canvas.axes.plot(data["x"],
data["y"],
marker="+",
linestyle="--")
self.update_legend()
self.idle()
+3
View File
@@ -55,6 +55,9 @@ class CustomPlotTranslate(ResultsTranslate):
self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"]
self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"]
self._dict['froude'] = self._dict["unit_froude"]
self._dict['user_imported'] = _translate(
"CustomPlot", "user_imported"
)
# Unit corresponding long name (plot axes display)
-2
View File
@@ -160,8 +160,6 @@ class TableModel(PamhyrTableModel):
self._lst = _river.reachs
elif self._opt_data == "profile" or self._opt_data == "raw_data":
self._lst = _river.reach(reach).profiles
# self._lst = list(compress(_river.reach(reach).profiles,
# _river.reach(reach).profile_mask))
elif self._opt_data == "solver":
self._lst = self._parent._solvers
+251 -57
View File
@@ -19,14 +19,35 @@
import os
import csv
import logging
# import rasterio
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
from numpy import sqrt
try:
import pyaudio
import wave
_audio_loaded = True
except Exception as e:
print(f"Either 'pyaudio' or 'wave' is not available: {e}")
_audio_loaded = False
from functools import reduce
import numpy as np
from datetime import datetime
from tools import trace, timer, logger_exception
from View.Tools.PamhyrWindow import PamhyrWindow
from View.WaitingDialog import WaitingDialog
from PyQt5.QtGui import (
QKeySequence, QIcon, QPixmap,
@@ -44,7 +65,7 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QSlider, QLabel, QWidget, QGridLayout, QTabBar
QSlider, QLabel, QWidget, QGridLayout, QTabBar, QInputDialog
)
from View.Tools.Plot.PamhyrCanvas import MplCanvas
@@ -337,10 +358,20 @@ class ResultsWindow(PamhyrWindow):
"action_add": self._add_custom_plot,
"action_export": self._export,
# "action_export": self.export_current,
"action_Geo_tiff": self.import_geotiff
"action_import_data": self.import_data,
"action_play_sound": self.play_sound
}
self.find(QAction, "action_Geo_tiff").setEnabled(False)
self.find(QAction, "action_Geo_tiff").setVisible(False)
if _rasterio_loaded:
actions["action_Geo_tiff"] = self.import_geotiff
else:
self.find(QAction, "action_Geo_tiff")\
.setEnabled(False)
if _audio_loaded:
actions["action_play_sound"] = self.play_sound
else:
self.find(QAction, "action_play_sound")\
.setVisible(False)
if len(self._results) > 1:
self.find(QAction, "action_reload").setEnabled(False)
@@ -703,7 +734,7 @@ class ResultsWindow(PamhyrWindow):
select_file="AnyFile",
callback=lambda f: self.export_to(f[0], x, y, envelop, solver_id),
default_suffix=".csv",
file_filter=["CSV (*.csv)"],
file_filter=[self._trad["file_csv"]],
)
def export_to(self, filename, x, y, envelop, solver_id):
@@ -944,7 +975,7 @@ class ResultsWindow(PamhyrWindow):
map(
lambda p:
p.get_ts_key(timestamp, "V") /
sqrt(9.81 * (
np.sqrt(9.81 * (
p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width(
@@ -958,7 +989,7 @@ class ResultsWindow(PamhyrWindow):
map(
lambda p:
p.get_ts_key(timestamp, "V") /
sqrt(9.81 * (
np.sqrt(9.81 * (
p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width(
@@ -971,7 +1002,7 @@ class ResultsWindow(PamhyrWindow):
map(
lambda p:
p.get_ts_key(timestamp, "V") /
sqrt(9.81 * (
np.sqrt(9.81 * (
p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width(
@@ -1107,7 +1138,7 @@ class ResultsWindow(PamhyrWindow):
if self._current_results != 2:
fr = my_dict[dict_y["froude"]] = list(
map(lambda z, v:
v / sqrt(9.81 * (
v / np.sqrt(9.81 * (
profile.geometry.wet_area(z) /
profile.geometry.wet_width(z))
), z, v)
@@ -1116,7 +1147,7 @@ class ResultsWindow(PamhyrWindow):
fr1 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
np.sqrt(9.81 * (
profile1.geometry.wet_area(z) /
profile1.geometry.wet_width(z))
), z1, v1)
@@ -1124,7 +1155,7 @@ class ResultsWindow(PamhyrWindow):
fr2 = list(
map(lambda z, v:
v /
sqrt(9.81 * (
np.sqrt(9.81 * (
profile2.geometry.wet_area(z) /
profile2.geometry.wet_width(z))
), z2, v2)
@@ -1169,48 +1200,211 @@ class ResultsWindow(PamhyrWindow):
self.update_table_selection_profile(profile_id)
def import_geotiff(self):
# options = QFileDialog.Options()
# settings = QSettings(QSettings.IniFormat,
# QSettings.UserScope, 'MyOrg', )
# options |= QFileDialog.DontUseNativeDialog
#
# file_types = [
# self._trad["file_geotiff"],
# self._trad["file_all"],
# ]
#
# filename, _ = QFileDialog.getOpenFileName(
# self,
# self._trad["open_file"],
# "",
# ";; ".join(file_types),
# options=options
# )
#
# if filename != "":
# with rasterio.open(filename) as data:
# img = data.read()
# b = data.bounds[:]
# # b[0] left
# # b[1] bottom
# # b[2] right
# # b[3] top
# xlim = self.canvas.axes.get_xlim()
# ylim = self.canvas.axes.get_ylim()
# if b[2] > b[0] and b[1] < b[3]:
# self.canvas.axes.imshow(img.transpose((1, 2, 0)),
# extent=[b[0], b[2], b[1], b[3]])
# else:
# dlg = CoordinatesDialog(
# xlim,
# ylim,
# trad=self._trad,
# parent=self
# )
# if dlg.exec():
# self.canvas.axes.imshow(img.transpose((1, 2, 0)),
# extent=dlg.values)
# self.plot_xy.idle()
# self.canvas.axes.set_xlim(xlim)
# self.canvas.axes.set_ylim(ylim)
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
file_types = [
self._trad["file_geotiff"],
self._trad["file_all"],
]
filename, _ = QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
)
if filename != "":
with rasterio.open(filename) as data:
img = data.read()
b = data.bounds[:]
# b[0] left
# b[1] bottom
# b[2] right
# b[3] top
xlim = self.canvas.axes.get_xlim()
ylim = self.canvas.axes.get_ylim()
if b[2] > b[0] and b[1] < b[3]:
self.canvas.axes.imshow(img.transpose((1, 2, 0)),
extent=[b[0], b[2], b[1], b[3]])
else:
dlg = CoordinatesDialog(
xlim,
ylim,
trad=self._trad,
parent=self
)
if dlg.exec():
self.canvas.axes.imshow(img.transpose((1, 2, 0)),
extent=dlg.values)
self.plot_xy.idle()
self.canvas.axes.set_xlim(xlim)
self.canvas.axes.set_ylim(ylim)
return
def import_data(self):
file_types = [
self._trad["file_csv"],
self._trad["file_all"],
]
self.file_dialog(
select_file="Existing_file",
callback=lambda f: self.read_csv_file(f[0]),
default_suffix=".csv",
file_filter=file_types,
)
def read_csv_file(self, filename):
if filename == "":
return
sep = " "
def is_float(string):
if string.replace(".", "").isnumeric():
return True
else:
return False
with open(filename, 'r', newline='') as f:
lines = f.readlines()
x = []
y = []
for line in lines:
if line[0] != "*" and line[0] != "#" and line[0] != "$":
row = line.split(sep)
if len(row) >= 2:
if is_float(row[0]) and is_float(row[1]):
x.append(float(row[0]))
y.append(float(row[1]))
data_type_lst = ['Q(t)', 'Z(t)', 'Z(x)']
data_type, ok = QInputDialog.getItem(
self, 'Data type', 'Chose the type of data:', data_type_lst)
if not ok:
return
legend, ok = QInputDialog.getText(self, 'Legend', 'Legend:')
if not ok:
return
if legend.strip() == '':
legend = '*'
tmp_dict = {'Z': 'water_elevation',
'Q': 'discharge',
'x': 'rk',
't': 'time'}
tmp_unit = {'Z': ' (m)',
'Q': ' (m³/s)'}
data = {'type_x': tmp_dict[data_type[2]],
'type_y': tmp_dict[data_type[0]],
'legend': legend,
'unit': tmp_unit[data_type[0]],
'x': x,
'y': y}
if data_type == 'Z(x)':
line = self.canvas_2.axes.plot(x, y, marker="+",
label=legend + ' (m)')
self.plot_rkc.canvas.draw_idle()
self.plot_rkc.update_idle()
if data_type == 'Q(t)':
line = self.canvas_4.axes.plot(x, y, marker="+",
label=legend + ' (m³/s)')
self.plot_h._line.append(line)
self.plot_h.enable_legend()
self.plot_h.canvas.draw_idle()
self.plot_h.update_idle
for p in self._additional_plot:
self._additional_plot[p].add_imported_plot(data)
def play_sound(self):
def get_sine_wave(frequency, duration,
sample_rate=44100, amplitude=4096):
t = np.linspace(0, duration, int(sample_rate*duration))
return amplitude*np.sin(2*np.pi*frequency*t)
if not _audio_loaded:
return
# results
results = self._results[self._current_results[0]]
if results is None:
return
# reach
reach = results.river.reachs[self._get_current_reach()]
profile_id = self._get_current_profile()
profile = reach.profile(profile_id)
t = self._timestamps
q = profile.get_key("Q")
scale = []
low = 30
high = 84
for k in range(low, high):
note = 440*2**((k-49)/12)
scale.append(note) # add musical note
n_notes = len(scale) # number of musical notes
# rescale
# we start at low to high
tmax = max(t)
tmin = min(t)
qmax = max(q)
qmin = min(q)
qfact = (n_notes) / (qmax - qmin + 1)
for i in range(len(q)):
q[i] = int((q[i] - qmin) * qfact)
nq = [q[0]] # (notes)
dq = [0.1] # durée
for i in range(len(t)-1):
if q[i] == q[i+1]:
dq[-1] += 0.1
else:
nq.append(q[i+1])
dq.append(0.1)
wq = []
volume = 2048
for i in range(len(dq)):
new_w = get_sine_wave(frequency=scale[nq[i]],
duration=dq[i],
amplitude=volume)
wq = np.concatenate((wq, new_w))
p = pyaudio.PyAudio()
chunk = 1048
stream = p.open(format=p.get_format_from_width(2),
channels=1,
rate=44100,
output=True)
def fn():
i = 0
while i < len(wq):
stream.write(
wq[i:min(i+chunk, len(wq))].astype(np.int16).tobytes()
)
i += chunk
dlg = WaitingDialog(
payload_fn=fn,
title="playing_sound",
parent=self
)
dlg.exec_()
stream.stop_stream()
stream.close()
p.terminate()
+2
View File
@@ -56,6 +56,8 @@ class ResultsTranslate(MainTranslate):
self._dict["file_all"] = _translate("Results", "All files (*)")
self._dict["file_geotiff"] = _translate(
"Results", "GeoTIFF file (*.tiff *.tif)")
self._dict["file_csv"] = _translate(
"Results", "CSV file (*.csv)")
self._dict["ImageCoordinates"] = _translate(
"Results", "Image coordinates"
)
-101
View File
@@ -138,107 +138,6 @@ class SelectSolverWindow(PamhyrDialog):
super(SelectSolverWindow, self).accept()
class CompareSolversWindow(PamhyrDialog):
_pamhyr_ui = "CompareSolvers"
_pamhyr_name = "Compare solvers"
def __init__(self, study=None, config=None,
parent=None):
self._solver1 = None
self._solver2 = None
name = _translate("Solver", "Compare solvers")
super(CompareSolversWindow, self).__init__(
title=name,
study=study,
config=config,
options=[],
parent=parent
)
self.setup_combobox1()
self.setup_combobox2()
self.setup_connections()
self.select_last_solver()
def setup_combobox1(self):
solvers = self._config.solvers
solvers_name = list(
map(
self._format_solver_name,
solvers
)
)
self.combobox_add_items("comboBox1", solvers_name)
def setup_combobox2(self):
solvers = self._config.solvers
solvers_name = list(
map(
self._format_solver_name,
solvers
)
)
self.combobox_add_items("comboBox2", solvers_name)
def setup_connections(self):
self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.reject)
def select_last_solver(self):
solvers = self._config.solvers
last = self._config.last_solver_name
solver = list(
filter(
lambda s: s.name == last,
solvers
)
)
if len(solver) != 0:
self.set_combobox_text(
"comboBox1",
self._format_solver_name(solver[0])
)
def _format_solver_name(self, solver):
return f"{solver.name} - ({solver._type})"
@property
def solver1(self):
return self._solver1
@property
def solver2(self):
return self._solver2
def accept(self):
solver_name1 = self.get_combobox_text("comboBox1")
solver_name1 = solver_name1.rsplit(" - ", 1)[0]
self._solver1 = next(
filter(
lambda s: s.name == solver_name1,
self._config.solvers
)
)
solver_name2 = self.get_combobox_text("comboBox2")
solver_name2 = solver_name2.rsplit(" - ", 1)[0]
self._solver2 = next(
filter(
lambda s: s.name == solver_name2,
self._config.solvers
)
)
super(CompareSolversWindow, self).accept()
class SolverLogWindow(PamhyrWindow):
_pamhyr_ui = "SolverLog"
_pamhyr_name = "Solver Log"
+6 -5
View File
@@ -186,12 +186,13 @@ class PamhyrWindow(ASubMainWindow, ListedSubWindow, PamhyrWindowTools):
def _set_title(self):
title = self._title
if self._study.is_read_only():
title = "⛔ " + title
if self._study is not None:
if self._study.is_read_only():
title = "⛔ " + title
scenario = self._study.status.scenario
if scenario is not None and scenario.name != "default":
title += f" | {scenario.name}"
scenario = self._study.status.scenario
if scenario is not None and scenario.name != "default":
title += f" | {scenario.name}"
self.ui.setWindowTitle(title)
+15
View File
@@ -228,6 +228,21 @@ class MainTranslate(UnitTranslate):
"Do you still want to open those results?"
)
self._dict["mb_diff_results_title"] = _translate(
"MainWindow", "Results compare"
)
self._dict["mb_diff_results_param_msg"] = _translate(
"MainWindow", "Results comparison parameters is invalid"
)
self._dict["mb_diff_results_compatibility_msg"] = _translate(
"MainWindow",
"Results comparison with two "
"incompatible study version"
)
self._dict["playing_sound"] = _translate(
"MainWindow", "Playing sound..."
)
self._dict["x"] = _translate("MainWindow", "X (m)")
self._dict["y"] = _translate("MainWindow", "Y (m)")
self._dict["Yes"] = _translate("MainWindow", "Yes")
+79
View File
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>117</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="locale">
<locale language="English" country="Europe"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="comboBoxSolver1"/>
</item>
<item>
<widget class="QComboBox" name="comboBoxSolver2"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QComboBox" name="comboBoxScenario1"/>
</item>
<item>
<widget class="QComboBox" name="comboBoxScenario2"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_ok">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+17 -6
View File
@@ -160,9 +160,9 @@
<string>&amp;Results</string>
</property>
<addaction name="action_menu_results_last"/>
<addaction name="action_open_results_from_file"/>
<addaction name="action_compare_results"/>
<addaction name="action_open_results_adists"/>
<addaction name="action_menu_open_results_from_file"/>
<addaction name="action_menu_compare_scenarios_results"/>
<addaction name="action_menu_open_results_adists"/>
</widget>
<widget class="QMenu" name="menu_help">
<property name="title">
@@ -726,7 +726,7 @@
<string>Edit hydraulic structures</string>
</property>
</action>
<action name="action_open_results_from_file">
<action name="action_menu_open_results_from_file">
<property name="enabled">
<bool>false</bool>
</property>
@@ -787,12 +787,15 @@
<string>DIF</string>
</property>
</action>
<action name="action_compare_results">
<action name="action_menu_compare_results">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Compare results</string>
</property>
</action>
<action name="action_open_results_adists">
<action name="action_menu_open_results_adists">
<property name="enabled">
<bool>false</bool>
</property>
@@ -800,6 +803,14 @@
<string>Open results AdisTS</string>
</property>
</action>
<action name="action_menu_compare_scenarios_results">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Compare results</string>
</property>
</action>
</widget>
<resources/>
<connections>
+26
View File
@@ -239,6 +239,8 @@
<addaction name="action_export"/>
<addaction name="action_reload"/>
<addaction name="action_Geo_tiff"/>
<addaction name="action_import_data"/>
<addaction name="action_play_sound"/>
</widget>
<action name="action_add">
<property name="icon">
@@ -288,6 +290,30 @@
<string>Import background image</string>
</property>
</action>
<action name="action_import_data">
<property name="icon">
<iconset>
<normaloff>ressources/import.png</normaloff>ressources/import.png</iconset>
</property>
<property name="text">
<string>Import data</string>
</property>
<property name="toolTip">
<string>Import data from SCV file</string>
</property>
</action>
<action name="action_play_sound">
<property name="icon">
<iconset>
<normaloff>ressources/multimedia-volume-control-symbolic.symbolic.png</normaloff>ressources/multimedia-volume-control-symbolic.symbolic.png</iconset>
</property>
<property name="text">
<string>play_sound</string>
</property>
<property name="toolTip">
<string>Play discharge as sound</string>
</property>
</action>
</widget>
<resources/>
<connections/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

+393 -195
View File
File diff suppressed because it is too large. Load diff