From 0076faf03a5c436e9f6478ee29404fb12371da66 Mon Sep 17 00:00:00 2001 From: Dylan Jeannin Date: Mon, 27 Jul 2026 09:31:29 +0200 Subject: [PATCH] Temperature: propagate TEM when splitting reach --- .../InitialConditionsTemperature.py | 39 +++++++++++++ .../InitialConditionsTemperatureList.py | 6 ++ .../InitialConditionsTemperatureSpec.py | 12 ++++ src/Model/River.py | 6 ++ .../WeatherParameters/WeatherParameters.py | 16 +++++ .../WeatherParametersList.py | 58 +++++++++++++++++++ src/Solver/AdisTT.py | 3 +- .../InitialConditionsTemperature/Table.py | 40 +++++++++++-- .../UndoCommand.py | 12 +++- .../InitialConditionsTemperature/Window.py | 7 +++ src/View/WeatherParameters/Table.py | 35 ++++++++++- src/View/WeatherParameters/UndoCommand.py | 18 +++++- src/View/WeatherParameters/Window.py | 7 +++ 13 files changed, 245 insertions(+), 14 deletions(-) diff --git a/src/Model/InitialConditionsTemperature/InitialConditionsTemperature.py b/src/Model/InitialConditionsTemperature/InitialConditionsTemperature.py index 350970c6..33e2c99e 100644 --- a/src/Model/InitialConditionsTemperature/InitialConditionsTemperature.py +++ b/src/Model/InitialConditionsTemperature/InitialConditionsTemperature.py @@ -227,3 +227,42 @@ class InitialConditionsTemperature(SQLSubModel): x.set_as_not_deleted() self.modified() + + def split_reach(self, reach, profile, reach1, reach2): + parts = [] + for new_reach in (reach1, reach2): + rks = new_reach.reach.get_rk() + if rks: + parts.append((new_reach.id, min(rks), max(rks))) + + specifications = [ + specification + for specification in self._data + if (not specification.is_deleted() and + specification.reach == reach.id) + ] + clones = [] + + for specification in specifications: + begin = specification.start_rk + end = specification.end_rk + if begin is None or end is None: + continue + + lower, upper = sorted((begin, end)) + reverse = begin > end + for new_reach, part_lower, part_upper in parts: + clipped_lower = max(lower, part_lower) + clipped_upper = min(upper, part_upper) + if clipped_lower > clipped_upper: + continue + + start_rk = clipped_upper if reverse else clipped_lower + end_rk = clipped_lower if reverse else clipped_upper + clones.append(specification.cloned_for( + new_reach, start_rk, end_rk + )) + + if clones: + self._data.extend(clones) + self.modified() diff --git a/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureList.py b/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureList.py index 2d60bc79..4dcdc9fd 100644 --- a/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureList.py +++ b/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureList.py @@ -62,6 +62,12 @@ class InitialConditionsTemperatureList(PamhyrModelList): self._status.modified() return n + def split_reach(self, reach, profile, reach1, reach2): + for initial_condition in self.lst: + initial_condition.split_reach( + reach, profile, reach1, reach2 + ) + @property def Initial_Conditions_List(self): return self.lst diff --git a/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureSpec.py b/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureSpec.py index 3682b275..431093fc 100644 --- a/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureSpec.py +++ b/src/Model/InitialConditionsTemperature/InitialConditionsTemperatureSpec.py @@ -200,3 +200,15 @@ class ICTemperatureSpec(SQLSubModel): def temperature(self, temperature): self._temperature = temperature self._status.modified() + + def cloned_for(self, reach, start_rk, end_rk): + new = ICTemperatureSpec( + name=self._name_section, + status=self._status + ) + new._reach = reach + new._start_rk = start_rk + new._end_rk = end_rk + new._temperature = self._temperature + new.modified() + return new diff --git a/src/Model/River.py b/src/Model/River.py index 00a0a799..35e4416f 100644 --- a/src/Model/River.py +++ b/src/Model/River.py @@ -994,5 +994,11 @@ Last export at: @date.""" self._LateralContributionsAdisTS.split_reach( reach, profile, r1, r2 ) + self._InitialConditionsTemperature.split_reach( + reach, profile, r1, r2 + ) + self._WeatherParameters.split_reach( + reach, profile, r1, r2 + ) return r1, r2 diff --git a/src/Model/WeatherParameters/WeatherParameters.py b/src/Model/WeatherParameters/WeatherParameters.py index b3637636..a440173a 100644 --- a/src/Model/WeatherParameters/WeatherParameters.py +++ b/src/Model/WeatherParameters/WeatherParameters.py @@ -153,6 +153,13 @@ class Data(SQLSubModel): def __setitem__(self, key, value): self._data[key] = self._types[key](value) + def cloned(self): + return Data( + self[0], self[1], + types=self._types, + status=self._status + ) + class WeatherParametersDefault(SQLSubModel): def __init__(self, type: str, value: float = 0.0, @@ -580,6 +587,15 @@ class WeatherParameters(SQLSubModel): self._end_section = section self.modified() + def cloned_for(self, reach, begin_section, end_section): + new = type(self)(name=self._name, status=self._status) + new._reach = reach + new._begin_section = begin_section + new._end_section = end_section + new._data = [data.cloned() for data in self.data] + new.modified() + return new + @property def header(self): return self._header.copy() diff --git a/src/Model/WeatherParameters/WeatherParametersList.py b/src/Model/WeatherParameters/WeatherParametersList.py index 135e7fde..399308ff 100644 --- a/src/Model/WeatherParameters/WeatherParametersList.py +++ b/src/Model/WeatherParameters/WeatherParametersList.py @@ -161,6 +161,64 @@ class WeatherParametersList(PamhyrModelList): self._status.modified() return n + def split_reach(self, reach, profile, reach1, reach2): + profiles = reach.reach.profiles + split_index = profiles.index(profile) + parts = [ + ( + reach1, + 0, + split_index, + dict(zip( + profiles[:split_index + 1], + reach1.reach.profiles + )) + ), + ( + reach2, + split_index, + len(profiles) - 1, + dict(zip( + profiles[split_index:], + reach2.reach.profiles + )) + ), + ] + weather_parameters = [ + weather_parameter + for weather_parameter in self.lst + if weather_parameter.reach is reach + ] + clones = [] + + for weather_parameter in weather_parameters: + if (weather_parameter.begin_section not in profiles or + weather_parameter.end_section not in profiles): + continue + + begin = profiles.index(weather_parameter.begin_section) + end = profiles.index(weather_parameter.end_section) + lower, upper = sorted((begin, end)) + reverse = begin > end + + for new_reach, part_lower, part_upper, sections in parts: + clipped_lower = max(lower, part_lower) + clipped_upper = min(upper, part_upper) + if clipped_lower > clipped_upper: + continue + + begin_index = clipped_upper if reverse else clipped_lower + end_index = clipped_lower if reverse else clipped_upper + clones.append(weather_parameter.cloned_for( + new_reach, + sections[profiles[begin_index]], + sections[profiles[end_index]] + )) + + if clones: + self._lst.extend(clones) + self._status.modified() + @property def Weather_Parameters_List(self): return self.lst diff --git a/src/Solver/AdisTT.py b/src/Solver/AdisTT.py index 9433230c..418a845d 100644 --- a/src/Solver/AdisTT.py +++ b/src/Solver/AdisTT.py @@ -935,7 +935,8 @@ class AdisTTwc(AdisTT): for weather_parameter in study.river.weather_parameters.lst: config = self._weather_files.get(weather_parameter.type) if (config is None or weather_parameter.reach is None - or weather_parameter.reach.is_deleted()): + or weather_parameter.reach.is_deleted() + or not weather_parameter.reach.is_enable()): continue _, extension = config exported = self._export_weather_file( diff --git a/src/View/InitialConditionsTemperature/Table.py b/src/View/InitialConditionsTemperature/Table.py index 1acafd00..c2a6fca8 100644 --- a/src/View/InitialConditionsTemperature/Table.py +++ b/src/View/InitialConditionsTemperature/Table.py @@ -140,13 +140,30 @@ class InitialConditionTableModel(PamhyrTableModel): self._data = data def _setup_lst(self): + enabled_reach_ids = { + reach.id + for reach in self._river.enable_edges() + } self._lst = list( filter( - lambda ica: ica._deleted is False, + lambda ica: ( + ica._deleted is False and + ( + ica.reach in (None, -1) or + ica.reach in enabled_reach_ids + ) + ), self._data._data ) ) + def refresh(self): + self.beginResetModel() + try: + self._setup_lst() + finally: + self.endResetModel() + def rowCount(self, parent): return len(self._lst) @@ -221,20 +238,31 @@ class InitialConditionTableModel(PamhyrTableModel): ) ) else: + new_value = value + if self._headers[column] == "reach": + reach = self._river.edge(value) + rks = reach.reach.get_rk() + if not rks: + return False + new_value = (reach.id, min(rks)) + self._undo.push( SetCommandSpec( self._lst, row, self._headers[column], - (self._river.edge(value).id - if self._headers[column] == "reach" - else value - ) + new_value ) ) except Exception as e: logger.info(e) logger.debug(traceback.format_exc()) - self.dataChanged.emit(index, index) + if self._headers[column] == "reach": + rk_index = self.index( + row, self._headers.index("rk") + ) + self.dataChanged.emit(index, rk_index) + else: + self.dataChanged.emit(index, index) return True def add(self, row, parent=QModelIndex()): diff --git a/src/View/InitialConditionsTemperature/UndoCommand.py b/src/View/InitialConditionsTemperature/UndoCommand.py index 83f93265..12de80a1 100644 --- a/src/View/InitialConditionsTemperature/UndoCommand.py +++ b/src/View/InitialConditionsTemperature/UndoCommand.py @@ -73,6 +73,8 @@ class SetCommandSpec(QUndoCommand): self._old = self._data[self._row].name elif self._column == "reach": self._old = self._data[self._row].reach + self._old_start_rk = self._data[self._row].start_rk + self._old_end_rk = self._data[self._row].end_rk elif self._column == "rk": self._old = self._data[self._row].start_rk elif self._column == "temperature": @@ -84,13 +86,19 @@ class SetCommandSpec(QUndoCommand): elif column == "reach": _type = int - self._new = _type(new_value) + if column == "reach": + self._new = _type(new_value[0]) + self._new_rk = float(new_value[1]) + else: + self._new = _type(new_value) def undo(self): if self._column == "name": self._data[self._row].name = self._old elif self._column == "reach": self._data[self._row].reach = self._old + self._data[self._row].start_rk = self._old_start_rk + self._data[self._row].end_rk = self._old_end_rk elif self._column == "rk": self._data[self._row].start_rk = self._old self._data[self._row].end_rk = self._old @@ -102,6 +110,8 @@ class SetCommandSpec(QUndoCommand): self._data[self._row].name = self._new elif self._column == "reach": self._data[self._row].reach = self._new + self._data[self._row].start_rk = self._new_rk + self._data[self._row].end_rk = self._new_rk elif self._column == "rk": self._data[self._row].start_rk = self._new self._data[self._row].end_rk = self._new diff --git a/src/View/InitialConditionsTemperature/Window.py b/src/View/InitialConditionsTemperature/Window.py index 53f45fe6..bdc7a0db 100644 --- a/src/View/InitialConditionsTemperature/Window.py +++ b/src/View/InitialConditionsTemperature/Window.py @@ -309,3 +309,10 @@ class InitialConditionsTemperatureWindow(PamhyrWindow): if len(rows) == 0: return self._table_spec.delete(rows) + + def _propagated_update(self, key=Modules(0)): + if Modules.NETWORK not in key: + return + + self._table_spec.refresh() + self.table_spec.clearSelection() diff --git a/src/View/WeatherParameters/Table.py b/src/View/WeatherParameters/Table.py index 8b394248..b3e05398 100644 --- a/src/View/WeatherParameters/Table.py +++ b/src/View/WeatherParameters/Table.py @@ -147,8 +147,28 @@ class WeatherParametersTableModel(PamhyrTableModel): if self._type == "ND" or self._data is None: self._lst = [] return + enabled_reach_ids = { + reach.id + for reach in self._river.enable_edges() + } self._lst = self._data.lst - self._lst = list(filter(lambda wp: wp.type == self._type, self._lst)) + self._lst = list(filter( + lambda wp: ( + wp.type == self._type and + ( + wp.reach is None or + wp.reach.id in enabled_reach_ids + ) + ), + self._lst + )) + + def refresh(self): + self.beginResetModel() + try: + self._setup_lst() + finally: + self.endResetModel() def update_tab_spec(self, type="ND", enabled=True): if enabled: @@ -242,16 +262,25 @@ class WeatherParametersTableModel(PamhyrTableModel): ) ) elif self._headers[column] == "reach": + edge = self._river.edge(value) + if not edge.reach.profiles: + return False self._undo.push( SetEdgeCommand( - self._data, global_row, self._river.edge(value) + self._data, global_row, edge ) ) except Exception as e: logger.info(e) logger.debug(traceback.format_exc()) - self.dataChanged.emit(index, index) + if self._headers[column] == "reach": + end_index = self.index( + row, self._headers.index("end_rk") + ) + self.dataChanged.emit(index, end_index) + else: + self.dataChanged.emit(index, index) return True def add(self, row, parent=QModelIndex()): diff --git a/src/View/WeatherParameters/UndoCommand.py b/src/View/WeatherParameters/UndoCommand.py index 18727d99..fe86f9b8 100644 --- a/src/View/WeatherParameters/UndoCommand.py +++ b/src/View/WeatherParameters/UndoCommand.py @@ -125,14 +125,26 @@ class SetEdgeCommand(QUndoCommand): self._wps = wps self._index = index - self._old = self._wps.get(self._index).reach + weather_parameter = self._wps.get(self._index) + self._old = weather_parameter.reach + self._old_begin = weather_parameter.begin_section + self._old_end = weather_parameter.end_section self._new = edge + profiles = edge.reach.profiles + self._new_begin = min(profiles, key=lambda profile: profile.rk) + self._new_end = max(profiles, key=lambda profile: profile.rk) def undo(self): - self._wps.get(self._index).reach = self._old + weather_parameter = self._wps.get(self._index) + weather_parameter.reach = self._old + weather_parameter.begin_section = self._old_begin + weather_parameter.end_section = self._old_end def redo(self): - self._wps.get(self._index).reach = self._new + weather_parameter = self._wps.get(self._index) + weather_parameter.reach = self._new + weather_parameter.begin_section = self._new_begin + weather_parameter.end_section = self._new_end class AddCommand(QUndoCommand): diff --git a/src/View/WeatherParameters/Window.py b/src/View/WeatherParameters/Window.py index 4730f7e5..5617eb5b 100644 --- a/src/View/WeatherParameters/Window.py +++ b/src/View/WeatherParameters/Window.py @@ -439,3 +439,10 @@ class WeatherParametersWindow(PamhyrWindow): parent=self ) win.show() + + def _propagated_update(self, key=Modules(0)): + if Modules.NETWORK not in key: + return + + self._table_spec.refresh() + self.table_spec.clearSelection()