Compare commits

...

40 Commits

Author SHA1 Message Date
Dylan Jeannin defcb3af84 BoundaryConditionsData: fix multidelete + relative undo/redo 2026-07-24 13:17:55 +02:00
Dylan Jeannin 5cf0a0ec76 Mesh: correction to make meshing thread-safe with tab display 2026-07-24 11:20:33 +02:00
Dylan Jeannin 1c44590def Meshing: unsigned distance to be able to mesh reach with start_rk > end_rk 2026-07-24 10:44:04 +02:00
Pierre-Antoine c09c62f077 Merge: Results reading optimisation. 2026-07-10 10:18:58 +02:00
Pierre-Antoine 7a2e93a995 Results: Custom plot: Minor change. 2026-07-10 09:16:05 +02:00
Pierre-Antoine 5327c29641 Results: Custom plot: Use 'zfd' np table. 2026-07-09 18:05:10 +02:00
Pierre-Antoine be865102a8 Results: Put 'zfd' data into np table. 2026-07-09 16:53:48 +02:00
Pierre-Antoine 37f1b43b93 Debug: Fix pep8. 2026-07-09 10:16:23 +02:00
Pierre-Antoine 16f0775783 Pamhyr: Update all copyright. 2026-07-09 10:13:06 +02:00
Pierre-Antoine f2c4285b4c Debug: Minor change. 2026-07-09 09:03:11 +02:00
Pierre-Antoine ee95f0e89e Debug: Add csv export method. 2026-07-08 17:55:31 +02:00
Pierre-Antoine 1306977954 Debug: Add Timer window. 2026-07-08 16:38:41 +02:00
Pierre-Antoine 476fa81bc2 Results: Plot{AC,RKC}: Fix max elevation and minor change. 2026-07-07 21:56:19 +02:00
Pierre-Antoine e8f1f0ac43 Results: Add TableData pamhyr object for np table. 2026-07-03 17:03:33 +02:00
Pierre-Antoine 1adf24ce77 Geometry: ProfileXYZ: Minor optimisation of computation. 2026-07-02 15:04:33 +02:00
Pierre-Antoine a61d130baa Solver: Mage: Comment setter Z, Q and V into profile and bufferize and fix indent. 2026-07-02 11:43:00 +02:00
Pierre-Antoine 0a1760cee6 Results: CustomPlot: Continue to switch to np table. 2026-07-02 11:00:51 +02:00
Pierre-Antoine 3e094af325 Results: CustomPlot: Use np table. 2026-07-01 17:54:34 +02:00
Pierre-Antoine 762e4d70a0 Results: Exports: Use np.{min,max}. 2026-07-01 17:54:08 +02:00
Pierre-Antoine 6c4f863b96 Results: Export: Use np table. 2026-07-01 15:39:52 +02:00
Pierre-Antoine b87f82f74d Results: PlotRKC: Use table to get results table. 2026-06-30 16:57:42 +02:00
Pierre-Antoine ffad53a331 Results: Table: Use results tables. 2026-06-30 15:30:44 +02:00
Pierre-Antoine 662a218e41 Results: Reach: Keep profiles global min and max. 2026-06-26 16:51:58 +02:00
Pierre-Antoine 0501ae9bec Solver: Mage: read_bin: Use np table for velocity computation. 2026-06-26 16:20:32 +02:00
Pierre-Antoine 7fd38eec00 Results: Table: Minor change. 2026-06-26 14:54:55 +02:00
Pierre-Antoine 4bab4580ad Results: PlotXY: Use table Z to display max elevation. 2026-06-26 14:41:24 +02:00
Pierre-Antoine 21d2e6ad20 Results: PlotAC: Use Z table. 2026-06-26 14:23:00 +02:00
Pierre-Antoine d0a8f369ef Results: PlotH: Use np table. 2026-06-26 14:05:29 +02:00
Pierre-Antoine fef2e1a40c Results: PlotXY: Minor change. 2026-06-26 10:51:36 +02:00
Pierre-Antoine 20c2cb7ea4 Merge branch 'master' into opt-result 2026-06-26 09:29:39 +02:00
Pierre-Antoine ecb2544cab Results: Keep timestamps index table. 2026-06-19 15:25:40 +02:00
Pierre-Antoine 9c66021b53 Results: Give access profiles to np table and keep profile global id. 2026-06-19 14:54:26 +02:00
Pierre-Antoine 8255fc3c1b River: Add some timer. 2026-06-19 09:23:42 +02:00
Pierre-Antoine 32e7413aad Solver: Mage: read_bin: Put velocity into np table. 2026-06-18 15:45:11 +02:00
Pierre-Antoine d87db5fd48 Solver: Mage: read_bin: Optimize iprofiles. 2026-06-18 15:02:34 +02:00
Pierre-Antoine 388368adad Solver: Mage: Add data np table to results. 2026-06-18 14:29:50 +02:00
Pierre-Antoine 4d014d5a1d Results: Minor change. 2026-06-18 10:11:17 +02:00
Pierre-Antoine 181a6c226f Geometry: ProfileXYZ: Add 'get_all_water_limits_ac' cache. 2026-06-18 10:09:51 +02:00
Pierre-Antoine 1975ed7eaa Profile: Create cache for 'get_water_limits' results. 2026-06-17 17:41:06 +02:00
Pierre-Antoine 565a7530fb Results: Minor results reading optimisation with async computation. 2026-06-17 17:04:58 +02:00
287 changed files with 1536 additions and 724 deletions

View File

@ -1,4 +1,4 @@
Sylvain COULIBALY, INRAE, 2020 - 2022 Sylvain COULIBALY, INRAE, 2020 - 2022
Théophile TERRAZ, INRAE, 2022 - 2024 Théophile TERRAZ, INRAE, 2022 - 2024
Pierre-Antoine ROUBY, INRAE, 2023 - 2025 Pierre-Antoine ROUBY, INRAE, 2023 - 2026
Dylan JEANNIN, INRAE, 2026 Dylan JEANNIN, INRAE, 2026

View File

@ -1,5 +1,5 @@
# Adists.py -- Pamhyr study checkers # Adists.py -- Pamhyr study checkers
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Checker.py -- Pamhyr abstract checker class # Checker.py -- Pamhyr abstract checker class
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Mage.py -- Pamhyr MAGE checkers # Mage.py -- Pamhyr MAGE checkers
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Study.py -- Pamhyr study checkers # Study.py -- Pamhyr study checkers
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# AMeshingTool.py -- Pamhyr # AMeshingTool.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Internal.py -- Pamhyr # Internal.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -57,6 +57,13 @@ class InternalMeshing(AMeshingTool):
# we never modify original profiles, we work on the copies # we never modify original profiles, we work on the copies
m_profiles = self.st_to_m(profiles, guide_list) m_profiles = self.st_to_m(profiles, guide_list)
# Adapt the spacing to the kilometre-point direction. The value from
# the dialog is a distance, so only its magnitude is significant.
step = self.signed_step(
step, m_profiles[0].rk, m_profiles[-1].rk
)
new_profiles = self.interpolate_transversal_step(m_profiles, step) new_profiles = self.interpolate_transversal_step(m_profiles, step)
for new_profiles2 in new_profiles: for new_profiles2 in new_profiles:
@ -65,6 +72,12 @@ class InternalMeshing(AMeshingTool):
return new_profiles return new_profiles
@staticmethod
def signed_step(step, begin_rk, end_rk):
if end_rk < begin_rk:
return -abs(step)
return abs(step)
def st_to_m(self, profiles, guide_list): def st_to_m(self, profiles, guide_list):
guide_list = ["un"] + guide_list + ["np"] guide_list = ["un"] + guide_list + ["np"]
max_values = [0] * (len(guide_list) - 1) max_values = [0] * (len(guide_list) - 1)

View File

@ -1,5 +1,5 @@
# Mage.py -- Pamhyr # Mage.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# __init__.py -- Pamhyr # __init__.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# test_Model.py -- Pamhyr # test_Model.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# BoundaryCondition.py -- Pamhyr # BoundaryCondition.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# BoundaryConditionList.py -- Pamhyr # BoundaryConditionList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# BoundaryConditionTypes.py -- Pamhyr # BoundaryConditionTypes.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# BoundaryConditionsAdisTS.py -- Pamhyr # BoundaryConditionsAdisTS.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# BoundaryConditionsAdisTSList.py -- Pamhyr # BoundaryConditionsAdisTSList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# D90AdisTS.py -- Pamhyr # D90AdisTS.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# D90AdisTSList.py -- Pamhyr # D90AdisTSList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# D90AdisTSSpec.py -- Pamhyr # D90AdisTSSpec.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# DIFAdisTS.py -- Pamhyr # DIFAdisTS.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# DIFAdisTSList.py -- Pamhyr # DIFAdisTSList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# DIFAdisTSSpec.py -- Pamhyr # DIFAdisTSSpec.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Except.py -- Pamhyr model exceptions # Except.py -- Pamhyr model exceptions
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Friction.py -- Pamhyr # Friction.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# FrictionList.py -- Pamhyr # FrictionList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Point.py -- Pamhyr # Point.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# PointXY.py -- Pamhyr # PointXY.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# PointXYZ.py -- Pamhyr # PointXYZ.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Profile.py -- Pamhyr # Profile.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# ProfileXYZ.py -- Pamhyr # ProfileXYZ.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -91,6 +91,9 @@ class ProfileXYZ(Profile, SQLSubModel):
self._station = [] self._station = []
self.station_up_to_date = False self.station_up_to_date = False
self._get_water_limits_cache = {}
self._get_water_limits_ac_cache = {}
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
execute(f""" execute(f"""
@ -619,6 +622,7 @@ class ProfileXYZ(Profile, SQLSubModel):
""" """
return [x for x in lst if not np.isnan(x)] return [x for x in lst if not np.isnan(x)]
@timer
def speed(self, q, z): def speed(self, q, z):
area = self.wet_area(z) area = self.wet_area(z)
@ -670,6 +674,7 @@ class ProfileXYZ(Profile, SQLSubModel):
length += line.length length += line.length
return length return length
@timer
def compute_wet_area(self, z): def compute_wet_area(self, z):
area = 0.0 area = 0.0
if len(self.tab.L) > 0: if len(self.tab.L) > 0:
@ -710,6 +715,7 @@ class ProfileXYZ(Profile, SQLSubModel):
if len(line.coords) > 2: if len(line.coords) > 2:
poly = geometry.Polygon(line) poly = geometry.Polygon(line)
area += poly.area area += poly.area
return area return area
def wet_radius(self, z): def wet_radius(self, z):
@ -732,6 +738,7 @@ class ProfileXYZ(Profile, SQLSubModel):
line = geometry.LineString(list(zip(station, zz))) line = geometry.LineString(list(zip(station, zz)))
return line return line
@timer
def wet_lines(self, z): def wet_lines(self, z):
points = self._points points = self._points
if len(points) < 3: if len(points) < 3:
@ -808,7 +815,6 @@ class ProfileXYZ(Profile, SQLSubModel):
return points return points
def get_nb_wet_areas(self, z): def get_nb_wet_areas(self, z):
n_zones = 0 n_zones = 0
points = self._points points = self._points
if points[0].z <= z: if points[0].z <= z:
@ -820,10 +826,13 @@ class ProfileXYZ(Profile, SQLSubModel):
return n_zones return n_zones
@timer
def get_all_water_limits_ac(self, z): def get_all_water_limits_ac(self, z):
""" """
Determine all water limits for z elevation. Determine all water limits for z elevation.
""" """
if z in self._get_water_limits_ac_cache:
return self._get_water_limits_ac_cache[z]
points = self._points points = self._points
if len(points) < 3: if len(points) < 3:
@ -856,69 +865,94 @@ class ProfileXYZ(Profile, SQLSubModel):
logger.error(f"ERROR in get_all_water_limits_ac") logger.error(f"ERROR in get_all_water_limits_ac")
return [], [] return [], []
return start, list(reversed(end)) res = start, list(reversed(end))
self._get_water_limits_ac_cache[z] = res
return res
@timer
def get_water_limits(self, z): def get_water_limits(self, z):
if z in self._get_water_limits_cache:
return self._get_water_limits_cache[z]
v = self.get_water_limits_compute(z)
return v
def get_water_limits_compute(self, z):
""" """
Determine left and right limits of water elevation. Determine left and right limits of water elevation.
""" """
# Get the index of first point with elevation lesser than water # Get the index of first point with elevation lesser than water
# elevation (for the right and left river side) # elevation (for the right and left river side)
i_left = -1 i_left = next(
i_right = -1 filter(
lambda i: self.point(i).z <= z,
range(self.number_points)
),
-1
)
for i in range(self.number_points): i_right = next(
if self.point(i).z <= z: filter(
i_left = i lambda i: self.point(i).z <= z,
break reversed(range(self.number_points))
),
for i in reversed(range(self.number_points)): -1
if self.point(i).z <= z: )
i_right = i
break
# Interpolate points at river left side # Interpolate points at river left side
if (i_left > 0): if (i_left > 0):
if abs(self.point(i_left - 1).z - self.point(i_left).z) < 1e-20: pi = self.point(i_left)
pt_left = self.point(i_left) pim1 = self.point(i_left - 1)
if abs(pim1.z - pi.z) < 1e-20:
pt_left = pi
else: else:
fact = (z - self.point(i_left).z) / \ fact = (z - pi.z) / (pim1.z - pi.z)
(self.point(i_left - 1).z - self.point(i_left).z) x = pi.x + fact * (pim1.x - pi.x)
x = self.point(i_left).x + fact * \ y = pi.y + fact * (pim1.y - pi.y)
(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") pt_left = PointXYZ(x=x, y=y, z=z, name="wl_left")
else: else:
pt_left = self.point(0) pt_left = self.point(0)
# Interpolate points at river right side # Interpolate points at river right side
if (i_right < self.number_points - 1): if (i_right < self.number_points - 1):
if abs(self.point(i_right + 1).z - self.point(i_right).z) < 1e-20: pi = self.point(i_right)
pt_right = self.point(i_right) pip1 = self.point(i_right + 1)
if abs(pip1.z - pi.z) < 1e-20:
pt_right = pi
else: else:
fact = (z - self.point(i_right).z) / \ fact = (z - pi.z) / (pip1.z - pi.z)
(self.point(i_right + 1).z - self.point(i_right).z) x = pi.x + fact * (pip1.x - pi.x)
x = self.point(i_right).x + fact * \ y = pi.y + fact * (pip1.y - pi.y)
(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") pt_right = PointXYZ(x=x, y=y, z=z, name="wl_right")
else: else:
pt_right = self.point(self.number_points - 1) pt_right = self.point(self.number_points - 1)
return pt_left, pt_right v = (pt_left, pt_right)
# Put results in cache
self._get_water_limits_cache[z] = v
# Create a generator to improve results data reading speed
yield v
@timer
def compute_tabulation(self): def compute_tabulation(self):
sorted_points = sorted(self._points, key=lambda p: p.z) sorted_points = sorted(self._points, key=lambda p: p.z)
self.tab.z = np.array([p.z for p in sorted_points], np.float64) self.tab.z = np.array([p.z for p in sorted_points], np.float64)
self.tab.L = np.zeros(len(self.tab.z), np.float64) self.tab.L = np.zeros(len(self.tab.z), np.float64)
self.tab.A = np.zeros(len(self.tab.z), np.float64) self.tab.A = np.zeros(len(self.tab.z), np.float64)
for i in range(1, len(self.tab.z)): for i in range(1, len(self.tab.z)):
self.tab.L[i] = self.compute_wet_width(self.tab.z[i]) self.tab.L[i] = self.compute_wet_width(self.tab.z[i])
dx = (self.tab.L[i] + self.tab.L[i-1])/2 dx = (self.tab.L[i] + self.tab.L[i-1])/2
dz = self.tab.z[i] - self.tab.z[i-1] dz = self.tab.z[i] - self.tab.z[i-1]
self.tab.A[i] = self.tab.A[i-1] + dz * dx self.tab.A[i] = self.tab.A[i-1] + dz * dx
self.tab_up_to_date = True self.tab_up_to_date = True

View File

@ -1,5 +1,5 @@
# Reach.py -- Pamhyr # Reach.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Vector_1d.py -- Pamhyr # Vector_1d.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# HydraulicStructures.py -- Pamhyr # HydraulicStructures.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Types.py -- Pamhyr # Types.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Value.py -- Pamhyr # Value.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# HydraulicStructures.py -- Pamhyr # HydraulicStructures.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# HydraulicStructuresList.py -- Pamhyr # HydraulicStructuresList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# InitialConditions.py -- Pamhyr # InitialConditions.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# InitialConditionsDict.py -- Pamhyr # InitialConditionsDict.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# InitialConditionsAdisTS.py -- Pamhyr # InitialConditionsAdisTS.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# InitialConditionsAdisTSList.py -- Pamhyr # InitialConditionsAdisTSList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# InitialConditionsAdisTSSpec.py -- Pamhyr # InitialConditionsAdisTSSpec.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# LateralContribution.py -- Pamhyr # LateralContribution.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# LateralContributionList.py -- Pamhyr # LateralContributionList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# LateralContributionTypes.py -- Pamhyr # LateralContributionTypes.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# LateralContributionAdisTS.py -- Pamhyr # LateralContributionAdisTS.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# LateralContributionsAdisTSList.py -- Pamhyr # LateralContributionsAdisTSList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Edge.py -- Pamhyr # Edge.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Graph.py -- Pamhyr # Graph.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Node.py -- Pamhyr # Node.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Point.py -- Pamhyr # Point.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# __init__.py -- Pamhyr # __init__.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# test_Network.py -- Pamhyr # test_Network.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# OutputRKAdists.py -- Pamhyr # OutputRKAdists.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Pollutants.py -- Pamhyr # Pollutants.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Reservoir.py -- Pamhyr # Reservoir.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# ReservoirList.py -- Pamhyr # ReservoirList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Results.py -- Pamhyr # Results.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -22,6 +22,8 @@ import numpy as np
from copy import deepcopy from copy import deepcopy
from datetime import datetime from datetime import datetime
from tools import timer
from Model.Scenario import Scenario from Model.Scenario import Scenario
from Model.Tools.PamhyrDB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
from Model.Results.River.River import River from Model.Results.River.River import River
@ -155,8 +157,185 @@ class AdditionalData(SQLSubModel):
return True return True
class TableData(SQLSubModel):
_sub_classes = []
def __init__(self, id=-1, study=None,
legend="", data=None,
owner_scenario=-1):
super(TableData, self).__init__(
id=id, status=study.status,
owner_scenario=owner_scenario
)
self._study = study
self._legend = legend
self._data = data
@property
def data(self):
return self._data
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE results_table_data{ext} (
{cls.create_db_add_pamhyr_id()},
result INTEGER NOT NULL,
legend TEXT NOT NULL,
unit TEXT NOT NULL,
data_shape_x INTEGER NOT NULL,
data_shape_y INTEGER NOT NULL,
data_shape_z INTEGER DEFAULT -1,
data BLOB NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
FOREIGN KEY(result) REFERENCES results(pamhyr_id),
PRIMARY KEY(pamhyr_id, result, legend, scenario)
)
""")
if ext != "":
return True
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
create = False
if major == "0" and int(minor) == 2:
if int(release) < 7 and not create:
cls._db_create(execute)
create = True
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
res = {}
study = data['study']
status = data['status']
scenario = data["scenario"]
result = data["result"]
rid = data["result_pid"]
table = execute(
"SELECT pamhyr_id, " +
"legend, unit, " +
"data_shape_x, data_shape_y, data_shape_z, " +
"data, " +
"scenario " +
"FROM results_table_data " +
f"WHERE scenario = {scenario.id} " +
f"AND result = {rid}"
)
for v in table:
it = iter(v)
pid = next(it)
legend = next(it)
unit = next(it)
data_shape_x = next(it)
data_shape_y = next(it)
data_shape_z = next(it)
data = next(it)
owner_scenario = next(it)
dtype = cls._unit_to_dtype(unit)
np_data = np.frombuffer(data, dtype=dtype)
if data_shape_y == -1:
np_data = np.reshape(np_data, data_shape_x)
elif data_shape_z == -1:
np_data = np.reshape(np_data, (data_shape_x,
data_shape_y))
else:
np_data = np.reshape(np_data, (data_shape_x,
data_shape_y,
data_shape_z))
new_table = cls(study=study,
legend=legend, data=np_data,
owner_scenario=owner_scenario)
res[legend] = new_table
return res
def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario:
return
shape_x = -1
shape_y = -1
shape_z = -1
shape = self._data.shape
if len(shape) == 2:
shape_x, shape_y = shape
elif len(shape == 1):
shape_x, = shape
elif len(shape == 3):
shape_x, shape_y, shape_z = shape
else:
logger.error(
f"Failed to parse numpy shape: {shape}"
)
execute(
"INSERT INTO " +
"results_table_data (pamhyr_id, result, " +
"legend, unit, " +
"data_shape_x, data_shape_y, data_shape_z, " +
"data, " +
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
self._pamhyr_id, data["result"],
self._legend, self._dtype_to_unit(self._data.dtype),
shape_x, shape_y, shape_z,
self._data,
self._owner_scenario
)
return True
@classmethod
def _unit_to_dtype(cls, unit):
t = {
"i32": np.int32, "i64": np.int64,
"u32": np.uint32, "u64": np.uint64,
"f32": np.float32, "f64": np.float64,
}
return t[unit]
def _dtype_to_unit(self, dtype):
t = {
np.dtype(np.int32): "i32", np.dtype(np.int64): "i64",
np.dtype(np.uint32): "u32", np.dtype(np.uint64): "u64",
np.dtype(np.float32): "f32", np.dtype(np.float64): "f64",
}
return t[dtype]
def table(self):
return self._data
def __len__(self):
return len(self._data)
def __getitem__(self, key):
if isinstance(key, tuple):
return self._data[key[0], key[1]]
self._date[key]
class Results(SQLSubModel): class Results(SQLSubModel):
_sub_classes = [River, AdditionalData] _sub_classes = [River, TableData, AdditionalData]
def __init__(self, id=-1, study=None, solver=None, def __init__(self, id=-1, study=None, solver=None,
repertory="", name="0"): repertory="", name="0"):
@ -167,7 +346,7 @@ class Results(SQLSubModel):
self._name = name self._name = name
self._study = study self._study = study
self._river = River(self._study) self._river = River(self._study, self)
self._solver = solver self._solver = solver
self._repertory = repertory self._repertory = repertory
@ -177,8 +356,11 @@ class Results(SQLSubModel):
"creation_date": datetime.now(), "creation_date": datetime.now(),
"study_revision": study.status.version, "study_revision": study.status.version,
"additional_data": [], "additional_data": [],
"table": {},
} }
self._ts_index = {}
if solver is not None: if solver is not None:
self.set("solver_type", solver._type) self.set("solver_type", solver._type)
@ -195,6 +377,7 @@ class Results(SQLSubModel):
def study(self): def study(self):
return self._study return self._study
@timer
def set(self, key, value): def set(self, key, value):
self._meta_data[key] = value self._meta_data[key] = value
@ -212,6 +395,16 @@ class Results(SQLSubModel):
def get(self, key): def get(self, key):
return self._meta_data[key] return self._meta_data[key]
def set_timestamp_index(self, index):
self._ts_index = index
def get_timestamp_id(self, ts):
return self._ts_index[ts]
def compute_timestamp_index(self):
for i, ts in enumerate(self._meta_data["timestamps"]):
self._ts_index[ts] = i
def reload(self): def reload(self):
return self._solver.results( return self._solver.results(
self._study, self._study,
@ -309,11 +502,20 @@ class Results(SQLSubModel):
new_results.set("study_revision", revision) new_results.set("study_revision", revision)
new_results.set("creation_date", creation_date) new_results.set("creation_date", creation_date)
data["result_pid"] = pid
data["result"] = new_results
table_data = TableData._db_load(execute, data)
new_results.set("table", table_data)
sf = ">" + ''.join(itertools.repeat("d", nb_timestamps)) sf = ">" + ''.join(itertools.repeat("d", nb_timestamps))
ts = struct.unpack(sf, timestamps_bytes) ts = struct.unpack(sf, timestamps_bytes)
new_results.set("timestamps", ts) new_results.set("timestamps", ts)
data["timestamps"] = sorted(ts) data["timestamps"] = sorted(ts)
new_results.compute_timestamp_index()
data["parent"] = new_results
new_results._river = River._db_load(execute, data) new_results._river = River._db_load(execute, data)
new_results.set( new_results.set(
@ -321,6 +523,20 @@ class Results(SQLSubModel):
AdditionalData._db_load(execute, data) AdditionalData._db_load(execute, data)
) )
# Update global index
i = 0
for reach in new_results._river.reachs:
nb = len(reach.profiles)
reach.set_global_index(range(i, i + nb + 1))
i += nb
if "Z" in table_data:
for ts in data["timestamps"]:
new_results._river.update_water_limits(
ts,
table_data["Z"][new_results.get_timestamp_id(ts), :]
)
yield (solver_type, new_results) yield (solver_type, new_results)
def _db_save_clear(self, execute, solver_type, data=None): def _db_save_clear(self, execute, solver_type, data=None):
@ -343,6 +559,11 @@ class Results(SQLSubModel):
f"WHERE scenario = {self._owner_scenario} " + f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}" f"AND result = {pid}"
) )
execute(
"DELETE FROM results_table_data " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}"
)
execute( execute(
"DELETE FROM results_add_data " + "DELETE FROM results_add_data " +
f"WHERE scenario = {self._owner_scenario} " + f"WHERE scenario = {self._owner_scenario} " +
@ -386,7 +607,19 @@ class Results(SQLSubModel):
data["result"] = self._pamhyr_id data["result"] = self._pamhyr_id
self._river._db_save(execute, data) self._river._db_save(execute, data)
tables = self.get("table")
for legend in tables:
tables[legend]._db_save(execute, data)
for add_data in self.get("additional_data"): for add_data in self.get("additional_data"):
add_data._db_save(execute, data) add_data._db_save(execute, data)
return True return True
def new_table_data(self, legend, data):
return TableData(
study=self._study,
legend=legend,
data=data,
owner_scenario=self._owner_scenario
)

View File

@ -1,5 +1,5 @@
# Results.py -- Pamhyr # Results.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# River.py -- Pamhyr # River.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -14,12 +14,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
import types
import struct import struct
import logging import logging
import itertools import itertools
import numpy as np import numpy as np
from tools import flatten from tools import flatten, timer
from functools import reduce from functools import reduce
from datetime import datetime from datetime import datetime
@ -30,15 +31,17 @@ logger = logging.getLogger()
class Profile(SQLSubModel): class Profile(SQLSubModel):
def __init__(self, profile, study): def __init__(self, profile, study, parent):
super(Profile, self).__init__( super(Profile, self).__init__(
id=-1, status=study.status, id=-1, status=study.status,
owner_scenario=study.status.scenario.id owner_scenario=study.status.scenario.id
) )
self._parent = parent
self._study = study self._study = study
self._profile = profile # Source profile in the study self._profile = profile # Source profile in the study
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...} self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
self._global_index = -1
def __len__(self): def __len__(self):
return len(self._data) return len(self._data)
@ -55,15 +58,26 @@ class Profile(SQLSubModel):
def geometry(self): def geometry(self):
return self._profile return self._profile
@property
def global_index(self):
return self._global_index
@global_index.setter
def global_index(self, id):
self._global_index = id
@timer
def set(self, timestamp, key, data): def set(self, timestamp, key, data):
if timestamp not in self._data: if timestamp not in self._data:
self._data[timestamp] = {} self._data[timestamp] = {}
self._data[timestamp][key] = data self._data[timestamp][key] = data
@timer
def get_ts(self, timestamp): def get_ts(self, timestamp):
return self._data[timestamp] return self._data[timestamp]
@timer
def get_key(self, key): def get_key(self, key):
res = list( res = list(
map(lambda ts: self._data[ts][key], map(lambda ts: self._data[ts][key],
@ -71,17 +85,26 @@ class Profile(SQLSubModel):
) )
return res return res
@timer
def get_ts_key(self, timestamp, key): def get_ts_key(self, timestamp, key):
if timestamp in self._data: if timestamp in self._data:
if key in self._data[timestamp]: if key in self._data[timestamp]:
return self._data[timestamp][key] v = self._data[timestamp][key]
# If is a generator, compute value(s)
if isinstance(v, types.GeneratorType):
v = next(v)
self._data[timestamp][key] = v
return v
return None return None
def has_sediment_layers(self): def has_sediment_layers(self):
return any(map(lambda ts: "sl" in self._data[ts], self._data)) return any(map(lambda ts: "sl" in self._data[ts], self._data))
def has_bedload(self): def has_bedload(self):
return any(map(lambda ts: "zfd" in self._data[ts], self._data)) return any(map(lambda ts: "sl" in self._data[ts], self._data))
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
@ -114,9 +137,9 @@ class Profile(SQLSubModel):
@classmethod @classmethod
def _db_load(cls, execute, data=None): def _db_load(cls, execute, data=None):
new = {}
status = data['status'] status = data['status']
parent = data['parent']
study = data['study'] study = data['study']
reach = data['reach'] reach = data['reach']
profile = data['profile'] profile = data['profile']
@ -124,6 +147,8 @@ class Profile(SQLSubModel):
loaded = data['loaded_pid'] loaded = data['loaded_pid']
timestamps = data['timestamps'] timestamps = data['timestamps']
new = cls(profile, study, parent)
values = execute( values = execute(
"SELECT pamhyr_id, result, key, " + "SELECT pamhyr_id, result, key, " +
"len_data, data, scenario " + "len_data, data, scenario " +
@ -143,12 +168,6 @@ class Profile(SQLSubModel):
data = next(it) data = next(it)
owner_scenario = next(it) owner_scenario = next(it)
if profile not in new:
new_data = cls(profile, study)
new[profile] = new_data
else:
new_data = new[profile]
if key in ["Z", "Q", "V"]: if key in ["Z", "Q", "V"]:
sf = ">" + ''.join(itertools.repeat("f", len_data)) sf = ">" + ''.join(itertools.repeat("f", len_data))
len_values = len(values) len_values = len(values)
@ -161,11 +180,11 @@ class Profile(SQLSubModel):
) )
for timestamp, value in zip(timestamps, values): for timestamp, value in zip(timestamps, values):
new_data.set(timestamp, key, value) new.set(timestamp, key, value)
if key == "Z": if key == "Z":
new_data.update_water_limits(timestamp, value) new.update_water_limits(timestamp, value)
return list(new.values()) return new
@classmethod @classmethod
def _db_load_data_sl_format(cls, values, len_data, timestamps): def _db_load_data_sl_format(cls, values, len_data, timestamps):
@ -257,19 +276,20 @@ class Profile(SQLSubModel):
class Reach(SQLSubModel): class Reach(SQLSubModel):
_sub_classes = [Profile] _sub_classes = [Profile]
def __init__(self, reach, study, with_init=True): def __init__(self, reach, study, parent, with_init=True):
super(Reach, self).__init__( super(Reach, self).__init__(
id=-1, status=study.status, id=-1, status=study.status,
owner_scenario=study.status.scenario.id owner_scenario=study.status.scenario.id
) )
self._parent = parent
self._study = study self._study = study
self._reach = reach # Source reach in the study self._reach = reach # Source reach in the study
self._profiles = [] self._profiles = []
if with_init: if with_init:
self._profiles = list( self._profiles = list(
map( map(
lambda p: Profile(p, self._study), lambda p: Profile(p, self._study, self._parent),
reach.profiles reach.profiles
) )
) )
@ -279,6 +299,9 @@ class Reach(SQLSubModel):
lambda p: p.name[0:8] != 'interpol', self._profiles lambda p: p.name[0:8] != 'interpol', self._profiles
) )
) )
self._global_index = (-1, -1)
self._buffers = {} self._buffers = {}
def __len__(self): def __len__(self):
@ -307,6 +330,17 @@ class Reach(SQLSubModel):
def profile(self, id): def profile(self, id):
return self._profiles[id] return self._profiles[id]
def set_global_index(self, indexs):
self._global_index = (indexs[0], indexs[-1])
for profile, ind in zip(self._profiles, indexs):
profile.global_index = ind
@property
def global_index(self):
return self._global_index
@timer
def set(self, profile_id, timestamp, key, data): def set(self, profile_id, timestamp, key, data):
self._profiles[profile_id].set(timestamp, key, data) self._profiles[profile_id].set(timestamp, key, data)
@ -322,6 +356,10 @@ class Reach(SQLSubModel):
for i, p in enumerate(self._profiles): for i, p in enumerate(self._profiles):
self._buffers[key][:, i] = p.get_key(key) self._buffers[key][:, i] = p.get_key(key)
def update_water_limits(self, timestamp, z_data):
for profile, z in zip(self._profiles, z_data):
profile.update_water_limits(timestamp, z)
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
return cls._create_submodel(execute) return cls._create_submodel(execute)
@ -335,12 +373,13 @@ class Reach(SQLSubModel):
reach = data["reach"] reach = data["reach"]
new_reach = cls( new_reach = cls(
data["reach"], data["study"], with_init=False data["reach"], data["study"], data["parent"],
with_init=False
) )
for i, profile in enumerate(reach.profiles): for i, profile in enumerate(reach.profiles):
data["profile"] = profile data["profile"] = profile
new_reach._profiles += Profile._db_load(execute, data) new_reach._profiles += [Profile._db_load(execute, data)]
return new_reach return new_reach
@ -354,13 +393,14 @@ class Reach(SQLSubModel):
class River(SQLSubModel): class River(SQLSubModel):
_sub_classes = [Reach] _sub_classes = [Reach]
def __init__(self, study): def __init__(self, study, parent):
super(River, self).__init__( super(River, self).__init__(
id=-1, status=study.status, id=-1, status=study.status,
owner_scenario=study.status.scenario.id owner_scenario=study.status.scenario.id
) )
self._study = study self._study = study
self._parent = parent
# Dict with timestamps as key # Dict with timestamps as key
self._reachs = [] self._reachs = []
@ -381,7 +421,7 @@ class River(SQLSubModel):
def add(self, reach_id): def add(self, reach_id):
reachs = self._study.river.enable_edges() reachs = self._study.river.enable_edges()
new = Reach(reachs[reach_id].reach, self._study) new = Reach(reachs[reach_id].reach, self._study, self._parent)
self._reachs.append(new) self._reachs.append(new)
return new return new
@ -394,6 +434,15 @@ class River(SQLSubModel):
) )
) )
def update_water_limits(self, timestamp, data):
for reach in self._reachs:
ind = reach.global_index
reach.update_water_limits(
timestamp,
data[ind[0]:ind[1]]
)
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
return cls._create_submodel(execute) return cls._create_submodel(execute)
@ -405,7 +454,9 @@ class River(SQLSubModel):
@classmethod @classmethod
def _db_load(cls, execute, data=None): def _db_load(cls, execute, data=None):
study = data["study"] study = data["study"]
new_river = cls(study) parent = data["parent"]
new_river = cls(study, parent)
for reach in study.river.reachs(): for reach in study.river.reachs():
data["reach"] = reach.reach data["reach"] = reach.reach

View File

@ -1,5 +1,5 @@
# River.py -- Pamhyr # River.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# River.py -- Pamhyr river model # River.py -- Pamhyr river model
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Serializable.py -- Pamhyr pickle abstract class # Serializable.py -- Pamhyr pickle abstract class
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# SolverParametersList.py -- Pamhyr # SolverParametersList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Status.py -- Pamhyr model status class # Status.py -- Pamhyr model status class
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Stricklers.py -- Pamhyr # Stricklers.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# StricklersList.py -- Pamhyr # StricklersList.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Study.py -- Pamhyr Study class # Study.py -- Pamhyr Study class
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -46,7 +46,7 @@ logger = logging.getLogger()
class Study(SQLModel): class Study(SQLModel):
_version = "0.2.6" _version = "0.2.7"
_sub_classes = [ _sub_classes = [
Scenario, Scenario,

View File

@ -1,5 +1,5 @@
# PamhyrDB.py -- Pamhyr abstract model database classes # PamhyrDB.py -- Pamhyr abstract model database classes
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# PamhyrDict.py -- Pamhyr abstract dict for model classes # PamhyrDict.py -- Pamhyr abstract dict for model classes
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# PamhyrList.py -- Pamhyr Abstract List object for the Model # PamhyrList.py -- Pamhyr Abstract List object for the Model
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# PamhyrList.py -- Pamhyr Abstract List object for the Model # PamhyrList.py -- Pamhyr Abstract List object for the Model
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# __init__.py -- Pamhyr # __init__.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# test_Model.py -- Pamhyr # test_Model.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# SQL.py -- Pamhyr # SQL.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# AScript.py -- Pamhyr abstract script class # AScript.py -- Pamhyr abstract script class
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Hello.py -- Pamhyr # Hello.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# ListSolver.py -- Pamhyr # ListSolver.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# MageMesh.py -- Pamhyr # MageMesh.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# P3DST.py -- Pamhyr # P3DST.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Run.py -- Pamhyr # Run.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,3 +1,20 @@
# mage_to_mascaret.py -- Pamhyr
# Copyright (C) 2026 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 -*-
#!/bin/env python3 #!/bin/env python3
import sys import sys
@ -336,6 +353,3 @@ if __name__ == "__main__":
print( 'Number of arguments:', len(sys.argv), 'arguments.') print( 'Number of arguments:', len(sys.argv), 'arguments.')
print( 'Argument List:', str(sys.argv)) print( 'Argument List:', str(sys.argv))
mage_to_mascaret(sys.argv[1]) mage_to_mascaret(sys.argv[1])

View File

@ -1,3 +1,20 @@
# mascaret_to_mage.py -- Pamhyr
# Copyright (C) 2026 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 -*-
#!/bin/env python3 #!/bin/env python3
import sys import sys

View File

@ -1,5 +1,5 @@
# ASolver.py -- Pamhyr # ASolver.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# AdisTS.py -- Pamhyr # AdisTS.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# CommandLine.py -- Pamhyr # CommandLine.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# GenericSolver.py -- Pamhyr # GenericSolver.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Mage.py -- Pamhyr # Mage.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -1083,7 +1083,7 @@ class Mage8(Mage):
newline() newline()
reachs = [] reachs = []
iprofiles = {} iprofiles = []
profile_len = [] profile_len = []
reach_offset = {} reach_offset = {}
@ -1098,15 +1098,18 @@ class Mage8(Mage):
i1 = data[2*i] - 1 i1 = data[2*i] - 1
i2 = data[2*i+1] - 1 i2 = data[2*i+1] - 1
# Keep profiles index of all river
r.set_global_index(range(i1, i2 + 1))
# Add profile id correspondance to reach # Add profile id correspondance to reach
key = (i1, i2) for key in range(i1, i2 + 1):
iprofiles[key] = r iprofiles.append(i)
# Profile ID offset # Profile ID offset
reach_offset[r] = i1 reach_offset[r] = i1
profile_len.append(i2-i1+1) profile_len.append(i2 - i1 + 1)
logger.debug(f"read_bin: iprofiles = {iprofiles}") logger.debug(f"read_bin: iprofiles = {len(iprofiles)}")
endline() endline()
@ -1124,16 +1127,6 @@ class Mage8(Mage):
# Data # Data
newline() newline()
def ip_to_r(i): return iprofiles[
next(
filter(
lambda k: k[0] <= i <= k[1],
iprofiles
)
)
]
def ip_to_ri(r, i): return i - reach_offset[r]
# check results and geometry consistency # check results and geometry consistency
for i, r in enumerate(reachs): for i, r in enumerate(reachs):
lp = len(r.profiles) lp = len(r.profiles)
@ -1144,6 +1137,8 @@ class Mage8(Mage):
return return
ts = set() ts = set()
tmp_table = {}
end = False end = False
while not end: while not end:
n = read_int(1)[0] n = read_int(1)[0]
@ -1152,18 +1147,21 @@ class Mage8(Mage):
f, dtype=np.byte, count=1)).decode() f, dtype=np.byte, count=1)).decode()
data = read_float(n) data = read_float(n)
# logger.debug(f"read_bin: timestamp = {timestamp} sec") if key not in tmp_table:
tmp_table[key] = []
tmp_table[key].append(data)
ts.add(timestamp) ts.add(timestamp)
if key in ["Z", "Q"]: if key in ["Z", "Q"]:
for i, d in enumerate(data): for i, d in enumerate(data):
# Get reach corresponding to profile ID # Get reach corresponding to profile ID
reach = ip_to_r(i) reach = reachs[iprofiles[i]]
# Get profile id in reach # Get profile id in reach
ri = ip_to_ri(reach, i) ri = i - reach_offset[reach]
# Set data for profile RI # Set data for profile RI
reach.set(ri, timestamp, key, d) # reach.set(ri, timestamp, key, d)
if key == "Z": if key == "Z":
profile = reach.profile(ri) profile = reach.profile(ri)
limits = profile.geometry.get_water_limits(d) limits = profile.geometry.get_water_limits(d)
@ -1176,6 +1174,15 @@ class Mage8(Mage):
endline() endline()
end = newline().size <= 0 end = newline().size <= 0
ts_index = {}
for i, t in enumerate(sorted(ts)):
ts_index[t] = i
results.set_timestamp_index(ts_index)
table = {}
for k in tmp_table:
table[k] = np.array(tmp_table[k])
results.set("timestamps", ts) results.set("timestamps", ts)
ts_list = sorted(ts) ts_list = sorted(ts)
logger.info(f"compute tab...") logger.info(f"compute tab...")
@ -1185,19 +1192,33 @@ class Mage8(Mage):
logger.info(f"compute velocity...") logger.info(f"compute velocity...")
velocity_arr = np.zeros((len(ts), len(iprofiles)))
for ti, t in enumerate(ts_list):
j = 0
for r in reachs: for r in reachs:
for t in ts_list: for pi, p in enumerate(r.profiles):
for i, p in enumerate(r.profiles):
v = p.geometry.speed( v = p.geometry.speed(
p.get_ts_key(t, "Q"), table["Q"][ti, p.global_index],
p.get_ts_key(t, "Z") table["Z"][ti, p.global_index],
) )
r.set(i, t, "V", v) # r.set(pi, t, "V", v)
velocity_arr[ti, j] = v
j += 1
table["V"] = velocity_arr
for legend in table:
table[legend] = results.new_table_data(
legend, table[legend]
)
results.set("table", table)
logger.info(f"read_bin: ... end with {len(ts)} timestamp read") logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
results.bufferize("Z") # results.bufferize("Z")
results.bufferize("Q") # results.bufferize("Q")
results.bufferize("V") # results.bufferize("V")
logger.info(f"reading time: '{time.time() - start}'") logger.info(f"reading time: '{time.time() - start}'")
@timer @timer
@ -1267,8 +1288,8 @@ class Mage8(Mage):
reachs.append(r) reachs.append(r)
# ID of first and last reach profiles # ID of first and last reach profiles
i1 = data[2*i] - 1 i1 = data[2 * i] - 1
i2 = data[2*i+1] - 1 i2 = data[2 * i + 1] - 1
# Add profile id correspondance to reach # Add profile id correspondance to reach
key = (i1, i2) key = (i1, i2)
@ -1322,6 +1343,7 @@ class Mage8(Mage):
newline() newline()
npts = read_int(n) npts = read_int(n)
endline() endline()
sum_npts = sum(npts) sum_npts = sum(npts)
logger.debug(f"read_gra: Number of points: {sum_npts}") logger.debug(f"read_gra: Number of points: {sum_npts}")
@ -1371,12 +1393,14 @@ class Mage8(Mage):
logger.info(f"compute river bed elevation...") logger.info(f"compute river bed elevation...")
zfd_lst = []
for r in reachs: for r in reachs:
z_min = r.geometry.get_z_min() z_min = r.geometry.get_z_min()
sls = list(map( sls = map(
lambda p: p.get_ts_key(ts_list[0], "sl")[0], lambda p: p.get_ts_key(ts_list[0], "sl")[0],
r.profiles r.profiles
)) )
z_br = list(map( z_br = list(map(
lambda z, sl: reduce( lambda z, sl: reduce(
lambda z, h: z - h[0], lambda z, h: z - h[0],
@ -1385,11 +1409,11 @@ class Mage8(Mage):
z_min, # Original geometry z_min, # Original geometry
sls # Original sediment layers sls # Original sediment layers
)) ))
for t in ts_list: for its, t in enumerate(ts_list):
sls = list(map( sls = map(
lambda p: p.get_ts_key(t, "sl")[0], lambda p: p.get_ts_key(t, "sl")[0],
r.profiles r.profiles
)) )
zfd = list(map( zfd = list(map(
lambda z, sl: reduce( lambda z, sl: reduce(
lambda z, h: z + h[0], lambda z, h: z + h[0],
@ -1398,8 +1422,13 @@ class Mage8(Mage):
z_br, # bedrock z_br, # bedrock
sls # Original sediment layers sls # Original sediment layers
)) ))
for i, p in enumerate(r.profiles):
r.set(i, t, "zfd", zfd[i]) zfd_lst.append(np.array(zfd))
table = results.get("table")
table["zfd"] = results.new_table_data(
"zfd", np.array(zfd_lst)
)
results.set("sediment_timestamps", ts) results.set("sediment_timestamps", ts)
logger.info(f"read_gra: ... end with {len(ts)} timestamp read") logger.info(f"read_gra: ... end with {len(ts)} timestamp read")

View File

@ -1,5 +1,5 @@
# Solvers.py -- Pamhyr # Solvers.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Window.py -- Pamhyr # Window.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# GenerateDialog.py -- Pamhyr # GenerateDialog.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Plot.py -- Pamhyr # Plot.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Table.py -- Pamhyr # Table.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -143,21 +143,43 @@ class TableModel(PamhyrTableModel):
self.update() self.update()
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) if not rows:
return
rows = list(map( rows = sorted(set(rows))
lambda r: self.get_true_data_row(r), rows = [
rows)) self.get_true_data_row(row)
for row in rows
]
# A multiple selection may contain non-contiguous rows. A model reset
# accurately represents this atomic undo command, unlike one
# beginRemoveRows() call spanning rows that are not all removed.
self.beginResetModel()
try:
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._data, rows self._data, rows
) )
) )
finally:
self.endRemoveRows() self.endResetModel()
self.update() self.update()
def undo(self):
self.beginResetModel()
try:
self._undo.undo()
finally:
self.endResetModel()
def redo(self):
self.beginResetModel()
try:
self._undo.redo()
finally:
self.endResetModel()
def sort(self, _reverse, parent=QModelIndex()): def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()

View File

@ -1,5 +1,5 @@
# UndoCommand.py -- Pamhyr # UndoCommand.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -97,12 +97,11 @@ class DelCommand(QUndoCommand):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._data = data self._data = data
self._rows = rows self._rows = sorted(set(rows))
self._bc = [
self._bc = [] self._data.get_i(row)
for row in rows: for row in self._rows
self._bc.append(self._data.get_i(row)) ]
self._bc.sort()
def undo(self): def undo(self):
for el in self._bc: for el in self._bc:
@ -120,13 +119,17 @@ class SortCommand(QUndoCommand):
self._data = data self._data = data
self._reverse = _reverse self._reverse = _reverse
self._old = self._data.data # Keep the order of the complete underlying list, including soft
self._indexes = None # deleted items. ``data`` only exposes visible items and therefore
# cannot reliably restore a sort after a delete/undelete operation.
self._old_positions = {
id(item): index
for index, item in enumerate(self._data._data)
}
def undo(self): def undo(self):
ll = self._data.data
self._data.sort( self._data.sort(
key=lambda x: self._indexes[ll.index(x)] key=lambda item: self._old_positions[id(item)]
) )
def redo(self): def redo(self):
@ -134,14 +137,6 @@ class SortCommand(QUndoCommand):
_reverse=self._reverse, _reverse=self._reverse,
key=lambda x: x[0] key=lambda x: x[0]
) )
if self._indexes is None:
self._indexes = list(
map(
lambda p: self._old.index(p),
self._data.data
)
)
self._old = None
class MoveCommand(QUndoCommand): class MoveCommand(QUndoCommand):

View File

@ -1,5 +1,5 @@
# Window.py -- Pamhyr # Window.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# translate.py -- Pamhyr # translate.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Table.py -- Pamhyr # Table.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# UndoCommand.py -- Pamhyr # UndoCommand.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Window.py -- Pamhyr # Window.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# translate.py -- Pamhyr # translate.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,5 +1,5 @@
# Plot.py -- Pamhyr # Plot.py -- Pamhyr
# Copyright (C) 2023-2025 INRAE # Copyright (C) 2023-2026 INRAE
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

Some files were not shown because too many files have changed in this diff Show More