Ensemble: Add sequential runs with RUG variation into mage export.

scenario-dev-pa
Pierre-Antoine 2026-08-20 15:41:23 +02:00
parent 807592eab2
commit b5432498c6
6 changed files with 256 additions and 3 deletions

View File

@ -20,6 +20,8 @@ import struct
import logging
import itertools
import numpy as np
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Scenario import Scenario
from Model.Ensembles.Function import Uniform
@ -200,8 +202,10 @@ class Ensemble(SQLSubModel):
return struct.unpack(data_format, bparams)
def get_sample(self, nb):
return self._function.get_sample(
self._params, nb
return np.nditer(
self._function.get_sample(
self._params, nb
)
)
@property

View File

@ -420,6 +420,19 @@ class Study(SQLModel):
def _save(self, progress=None):
progress = progress if progress is not None else lambda: None
if self.status.scenario.is_ensemble():
self._save_submodel(
[
self.scenarios, self._ens_functions,
self._river._ensembles
],
data=progress
)
progress()
self.commit()
return
self.execute(
"INSERT OR REPLACE INTO info VALUES ('study_release', " +
f"'{self.status.version}')"

View File

@ -1027,6 +1027,68 @@ class Mage8(Mage):
logger_exception(e)
return False
def export_ensemble_variation(self, study, repertory,
qlog=None, name="0",
samples=None):
self._study = study
name = study.name.replace(" ", "_")
self._export_ensemble_RUG(
study, repertory,
qlog, name=name,
samples=samples,
)
return True
def _export_ensemble_RUG(self, study, repertory,
qlog, name="0",
samples={}):
files = []
for ens in samples:
if ens.target_data is None:
continue
if ens.data_type == "strickler_minor":
ens.target_data.minor = next(samples[ens])
elif ens.data_type == "strickler_medium":
ens.target_data.medium = next(samples[ens])
if qlog is not None:
qlog.put("Export variation of RUG file")
# Write header
with mage_file_open(os.path.join(repertory, f"{name}.RUG"), "w+") as f:
files.append(f"{name}.RUG")
edges = study.river.enable_edges()
id = 1
for edge in edges:
frictions = edge.frictions
for friction in frictions.frictions:
if friction.begin_strickler is None:
continue
if friction.is_deleted():
continue
num = f"{id:>3}"
brk = f"{friction.begin_rk:>10.3f}"
erk = f"{friction.end_rk:>10.3f}"
strickler = friction.begin_strickler
coef_1 = f"{strickler.minor:>10.3f}"
coef_2 = f"{strickler.medium:>10.3f}"
f.write(f"K{num} {brk}{erk}{coef_1}{coef_2}\n")
id += 1
return files
###########
# RESULTS #
###########

View File

@ -199,6 +199,7 @@ class EnsemblesWindow(PamhyrWindow):
logger_exception(e)
return
class MinMaxDialog(PamhyrDialog):
_pamhyr_ui = "MinMaxDialog"
_pamhyr_name = "Minmax"

View File

@ -83,10 +83,13 @@ from View.GeoTIFF.Window import GeoTIFFListWindow
from View.REPLines.Window import REPLineListWindow
from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import (
EnsembleRunNumberDialog,
SelectSolverWindow,
SolverLogWindow,
SolverLogEnsWindow,
)
from View.Results.Window import ResultsWindow
from View.Results.CompareDialog import (
CompareScenariosWindow
@ -1587,6 +1590,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
sol.show()
def solver_log(self, solver):
if self._study.status.scenario.is_ensemble():
return self._solver_log_ensemble(solver)
sol = SolverLogWindow(
study=self._study,
config=self.conf,
@ -1595,6 +1601,25 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
sol.show()
def _solver_log_ensemble(self, solver):
run_number = 1
dlg = EnsembleRunNumberDialog(
trad=self._trad,
parent=self
)
if dlg.exec():
run_number = dlg._number
sol = SolverLogEnsWindow(
run_number=run_number,
study=self._study,
config=self.conf,
solver=solver,
parent=self
)
sol.show()
def msg_box_results_continue(self):
dlg = QMessageBox(self)

View File

@ -40,7 +40,7 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QTextEdit,
QTextEdit, QProgressBar,
)
from View.RunSolver.Log.Window import SolverLogFileWindow
@ -57,6 +57,36 @@ _translate = QCoreApplication.translate
logger = logging.getLogger()
class EnsembleRunNumberDialog(PamhyrDialog):
_pamhyr_ui = "EnsembleRunNumberDialog"
_pamhyr_name = "EnsembleRunNumber"
def __init__(self, trad=None, parent=None):
super(EnsembleRunNumberDialog, self).__init__(
title=trad[self._pamhyr_name],
trad=trad,
options=[],
parent=parent
)
self._init_default_values()
def _init_default_values(self):
self.set_spin_box("spinBox", 100)
@property
def number(self):
return self._number
def accept(self):
self._number = self.get_spin_box("spinBox")
super().accept()
def reject(self):
self.close()
class SelectSolverWindow(PamhyrDialog):
_pamhyr_ui = "SelectSolver"
_pamhyr_name = "Select solver"
@ -425,3 +455,121 @@ class SolverLogWindow(PamhyrWindow):
parent=self,
)
log.show()
class SolverLogEnsWindow(SolverLogWindow):
_pamhyr_ui = "SolverLogEnsemble"
_pamhyr_name = "Solver Log Ensemble"
def __init__(self, run_number=100,
study=None, config=None,
solver=None, parent=None):
self._run_number = run_number
self._last_run = 0
self._samples = {}
for ens in study.river._ensembles.lst:
if ens.function is None:
continue
self._samples[ens] = ens.get_sample(self._run_number)
super(SolverLogEnsWindow, self).__init__(
solver=solver,
study=study,
config=config,
parent=parent,
)
def setup_connections(self):
self.setup_progress_bar()
super(SolverLogEnsWindow, self).setup_connections()
def setup_progress_bar(self):
self._progress_bar = self.find(QProgressBar, "progressBar")
self._progress_bar.setRange(0, self._run_number)
self._progress_bar.setValue(0)
def _log_int(self, int_code, color=None):
logger.info(f"solver: Returns {int_code}")
color = "blue" if int_code == 0 else "red"
self.find(QTextEdit, "textEdit")\
.append(
f"<font color=\"{color}\">" +
f" *** Finished with code {int_code}" +
"</font>"
)
self.statusbar.showMessage(
"Done" if int_code == 0 else "Failed",
3000
)
if self._last_run < self._run_number:
self.next()
else:
self._progress_bar.setValue(self._last_run)
def next(self):
self._process = self.new_process(self._parent)
self._log(f" *** Run #{self._last_run}", color="blue")
self._solver.export_ensemble_variation(
self._study, self._workdir, qlog=self._output,
samples=self._samples,
)
self._solver.run(
self._study,
process=self._process,
output_queue=self._output
)
self._last_run += 1
self._progress_bar.setValue(self._last_run - 1)
def run(self):
self._log(f" *** Run solver {self._solver.name}", color="blue")
self.next()
def start(self):
if self._solver.is_stoped():
self._log(f" *** Export study {self._solver.name}", color="blue")
ok = self._solver.export(
self._study, self._workdir, qlog=self._output
)
if not ok:
self._log(f" *** Failed to export", color="red")
self.update()
return
else:
self.update()
self._process = self.new_process(self._parent)
self._log(" *** Start", color="blue")
self._results = None
self.next()
self.find(QAction, "action_start").setEnabled(False)
if _signal:
self.find(QAction, "action_pause").setEnabled(True)
else:
self.find(QAction, "action_pause").setEnabled(False)
self.find(QAction, "action_stop").setEnabled(True)
self.find(QAction, "action_log_file").setEnabled(False)
self.find(QAction, "action_results").setEnabled(False)
def stop(self):
self._log(" *** Stop", color="blue")
self._solver.kill()
self.find(QAction, "action_start").setEnabled(True)
self.find(QAction, "action_pause").setEnabled(False)
self.find(QAction, "action_stop").setEnabled(False)
self.find(QAction, "action_results").setEnabled(True)
if self._solver.log_file() != "":
self.find(QAction, "action_log_file").setEnabled(True)