Merge branch 'scenarios' of gitlab.com:pamhyr/pamhyr2 into scenarios

test_sound
Theophile Terraz 2025-10-31 09:38:15 +01:00
commit 8bd791b2c4
15 changed files with 654 additions and 249 deletions

View File

@ -582,10 +582,10 @@ class BoundaryCondition(SQLSubModel):
self._data.index(bc) self._data.index(bc)
def get_i(self, index): def get_i(self, index):
if len(self.data) == 0: if len(self._data) == 0:
return None return None
return self.data[index] return self._data[index]
def get_range(self, _range): def get_range(self, _range):
lst = [] lst = []

View File

@ -52,6 +52,9 @@ class Results(SQLSubModel):
"study_revision": study.status.version, "study_revision": study.status.version,
} }
if solver is not None:
self.set("solver_type", solver._type)
@property @property
def date(self): def date(self):
date = self._meta_data["creation_date"] date = self._meta_data["creation_date"]
@ -112,8 +115,11 @@ class Results(SQLSubModel):
) )
""") """)
if ext != "":
return True return True
return cls._create_submodel(execute)
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
@ -147,7 +153,7 @@ class Results(SQLSubModel):
) )
if table is None: if table is None:
yield new yield
if len(table) > 1: if len(table) > 1:
logger.warning("Multiple results for this scenario") logger.warning("Multiple results for this scenario")
@ -178,23 +184,33 @@ class Results(SQLSubModel):
data["timestamps"] = sorted(ts) data["timestamps"] = sorted(ts)
new_results._river = River._db_load(execute, data) new_results._river = River._db_load(execute, data)
new = new_results yield (solver_type, new_results)
yield new def _db_save_clear(self, execute, solver_type, data=None):
old_pid = execute(
"SELECT pamhyr_id FROM results " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND solver_type = '{solver_type}'"
)
if len(old_pid) != 0:
for pid in old_pid:
pid = pid[0]
execute(
"DELETE FROM results " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND solver_type = '{solver_type}'"
)
execute(
"DELETE FROM results_data " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}"
)
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario: if self._status.scenario.id != self._owner_scenario:
return return
execute(
"DELETE FROM results " +
f"WHERE scenario = {self._owner_scenario}"
)
execute(
"DELETE FROM results_data " +
f"WHERE scenario = {self._owner_scenario}"
)
pid = self._pamhyr_id pid = self._pamhyr_id
if self._solver is None: if self._solver is None:
solver_name = self.get("solver_name") solver_name = self.get("solver_name")
@ -203,6 +219,8 @@ class Results(SQLSubModel):
solver_name = self._solver._name solver_name = self._solver._name
solver_type = self._solver._type solver_type = self._solver._type
self._db_save_clear(execute, solver_type, data=data)
ts = sorted(self.get("timestamps")) ts = sorted(self.get("timestamps"))
sf = ">" + ''.join(itertools.repeat("d", len(ts))) sf = ">" + ''.join(itertools.repeat("d", len(ts)))

View File

@ -227,7 +227,7 @@ class Profile(SQLSubModel):
values = list(map(float, values)) values = list(map(float, values))
sf = ">" + ''.join(itertools.repeat("f", len(values))) sf = ">" + ''.join(itertools.repeat("f", len(values)))
len_values = len(values) len_values = len(values)
elif key is "sl": elif key == "sl":
# HACK: Some dirty code to transforme list of list of # HACK: Some dirty code to transforme list of list of
# tuple to list of values and ensure the values is # tuple to list of values and ensure the values is
# float type... # float type...

View File

@ -505,7 +505,7 @@ class River(Graph):
self._D90AdisTS = D90AdisTSList(status=self._status) self._D90AdisTS = D90AdisTSList(status=self._status)
self._DIFAdisTS = DIFAdisTSList(status=self._status) self._DIFAdisTS = DIFAdisTSList(status=self._status)
self._results = None self._results = {}
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
@ -620,7 +620,10 @@ class River(Graph):
return new return new
def _db_load_results(self, execute, data=None): def _db_load_results(self, execute, data=None):
self._results = Results._db_load(execute, data) results_lst = Results._db_load(execute, data)
for solv_type, results in results_lst:
self._results[solv_type] = results
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
self._db_save_delete_artefact(execute, data) self._db_save_delete_artefact(execute, data)
@ -647,8 +650,8 @@ class River(Graph):
objs.append(self._D90AdisTS) objs.append(self._D90AdisTS)
objs.append(self._DIFAdisTS) objs.append(self._DIFAdisTS)
if self.results is not None: for solv_type in self.results:
objs.append(self.results) objs.append(self.results[solv_type])
self._save_submodel(execute, objs, data) self._save_submodel(execute, objs, data)
return True return True
@ -902,7 +905,9 @@ Last export at: @date."""
@results.setter @results.setter
def results(self, results): def results(self, results):
self._results = results solv_type = results.get("solver_type")
self._results[solv_type] = results
def _split_reach(self, reach, profile): def _split_reach(self, reach, profile):
node1 = reach.node1 node1 = reach.node1

View File

@ -276,7 +276,7 @@ class Scenario(SQLSubModel):
if self.id != 0: if self.id != 0:
srep = os.path.join( srep = os.path.join(
self.parent.workdir(), self.parent.workdir(),
"senario_" + str(self.id) "scenario_" + str(self.id)
) )
return srep return srep

View File

@ -44,7 +44,10 @@ class Study(SQLModel):
River, River,
] ]
def __init__(self, filename=None, init_new=True): def __init__(self, filename=None, init_new=True, copy=False):
if copy:
return
# Metadata # Metadata
self.creation_date = datetime.now() self.creation_date = datetime.now()
self.last_modification_date = datetime.now() self.last_modification_date = datetime.now()
@ -476,30 +479,7 @@ class Study(SQLModel):
return new return new
def reload_from_scenario(self, scenario): def reload_from_scenario(self, scenario):
if scenario in self._river_scenario_cache: river = self.load_scenario(scenario)
self._river = self._river_scenario_cache[scenario]
self.status.scenario = scenario
else:
def sql_exec(sql):
return self.execute(
sql, fetch_one=False, commit=True
)
self.status.scenario = scenario
data = {
"status": self.status,
"loaded_pid": set(),
"scenario": scenario
}
# Reload river data
river = River._db_load(
sql_exec, data=data
)
data["study"] = self
river._db_load_results(sql_exec, data=data)
self._river_scenario_cache[scenario] = river
self._river = river self._river = river
if reduce( if reduce(
@ -511,6 +491,80 @@ class Study(SQLModel):
else: else:
self.status.set_as_editable() self.status.set_as_editable()
def load_scenario(self, scenario):
if scenario in self._river_scenario_cache:
return self._river_scenario_cache[scenario]
def sql_exec(sql):
return self.execute(
sql, fetch_one=False, commit=True
)
old_scenario = self.status.scenario
self.status.scenario = scenario
data = {
"status": self.status,
"loaded_pid": set(),
"scenario": scenario
}
# Load river data
river = River._db_load(
sql_exec, data=data
)
data["study"] = self
river._db_load_results(sql_exec, data=data)
self._river_scenario_cache[scenario] = river
self.status.scenario = old_scenario
return river
def copy_from_scenario(self, scenario):
new = self._copy()
new._river = self.load_scenario(scenario)
def set_status(obj):
obj._status = new.status
new.river._data_traversal(
modifier=lambda obj, data: set_status(obj),
)
return new
def _copy(self):
"""
This method make a copy of current study. This copy is like an
empty shell, it's not fully functional. Study object use
SQLite connection to file, this copy as no valid connection.
(!) Please use this copy as read only object!
"""
new = Study(copy=True)
new._filename = ""
new.creation_date = self.creation_date
new.last_modification_date = self.last_modification_date
new.last_save_date = self.last_save_date
new._name = self._name
new.description = self.description
new._time_system = self._time_system
new._date = self._date
new.status = StudyStatus()
new.scenarios = self.scenarios
new.status.scenario = self.status.scenario
new._river = self._river
new._river_scenario_cache = self._river_scenario_cache
return new
def duplicate_current_scenario(self): def duplicate_current_scenario(self):
source = self.status.scenario_id source = self.status.scenario_id
new = self.scenarios.new( new = self.scenarios.new(

View File

@ -177,15 +177,15 @@ class Mage(CommandLineSolver):
return lst return lst
def input_param(self): def input_param(self):
name = self._study.name name = self._study.name.replace(" ", "_")
return f"{name}.REP" return f"{name}.REP"
def output_param(self): def output_param(self):
name = self._study.name name = self._study.name.replace(" ", "_")
return f"{name}.BIN" return f"{name}.BIN"
def log_file(self): def log_file(self):
name = self._study.name name = self._study.name.replace(" ", "_")
return f"{name}.TRA" return f"{name}.TRA"
def _export_REP_additional_lines(self, study, rep_file): def _export_REP_additional_lines(self, study, rep_file):
@ -819,7 +819,8 @@ class Mage(CommandLineSolver):
repertory=repertory, repertory=repertory,
name=name, name=name,
) )
fname = os.path.join(repertory, f"{name}.BIN")
fname = os.path.join(repertory, f"{name}.BIN".replace(" ", "_"))
if not os.path.isfile(fname): if not os.path.isfile(fname):
logger.info(f"Result file {name}.BIN does not exist") logger.info(f"Result file {name}.BIN does not exist")
return None return None
@ -1374,6 +1375,7 @@ class Mage8(Mage):
results = super(Mage8, self).results(study, repertory, qlog, name=name) results = super(Mage8, self).results(study, repertory, qlog, name=name)
if results is None: if results is None:
return None return None
if with_gra: if with_gra:
self.read_gra(study, repertory, results, qlog, name=name) self.read_gra(study, repertory, results, qlog, name=name)

View File

@ -58,7 +58,7 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel): class TableModel(PamhyrTableModel):
def get_true_data_row(self, row): def get_true_data_row(self, row):
bc = self._data.get_i(row) bc = self._data.data[row]
return next( return next(
map( map(
@ -83,6 +83,7 @@ class TableModel(PamhyrTableModel):
value = QVariant() value = QVariant()
if 0 <= column < 2: if 0 <= column < 2:
row = self.get_true_data_row(row)
v = self._data.get_i(row)[column] v = self._data.get_i(row)[column]
if self._data.get_type_column(column) == float: if self._data.get_type_column(column) == float:
if type(v) is str: if type(v) is str:
@ -106,13 +107,16 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
try: try:
row = self.get_true_data_row(row)
self._undo.push( self._undo.push(
SetDataCommand( SetDataCommand(
self._data, row, column, value self._data, row,
column, value
) )
) )
except Exception as e: except Exception as e:
logger.info(e) logger.warning(e)
logger.debug(traceback.format_exc()) logger.debug(traceback.format_exc())
self.update() self.update()

View File

@ -83,13 +83,13 @@ class AddCommand(QUndoCommand):
self._new = None self._new = None
def undo(self): def undo(self):
self._data.delete_i([self._index]) self._new.set_as_deleted()
def redo(self): def redo(self):
if self._new is None: if self._new is None:
self._new = self._data.add(self._index) self._new = self._data.add(self._index)
else: else:
self._data.insert(self._index, self._new) self._new.set_as_not_deleted()
class DelCommand(QUndoCommand): class DelCommand(QUndoCommand):
@ -101,15 +101,16 @@ class DelCommand(QUndoCommand):
self._bc = [] self._bc = []
for row in rows: for row in rows:
self._bc.append((row, self._data.get_i(row))) self._bc.append(self._data.get_i(row))
self._bc.sort() self._bc.sort()
def undo(self): def undo(self):
for row, el in self._bc: for el in self._bc:
self._data.insert(row, el) el.set_as_not_deleted()
def redo(self): def redo(self):
self._data.delete_i(self._rows) for el in self._bc:
el.set_as_deleted()
class SortCommand(QUndoCommand): class SortCommand(QUndoCommand):

View File

@ -32,7 +32,7 @@ from platformdirs import user_cache_dir
from Solver.AdisTS import AdisTS from Solver.AdisTS import AdisTS
from Solver.Mage import Mage8 from Solver.Mage import Mage8
from Solver.RubarBE import Rubar3 from Solver.RubarBE import Rubar3
from tools import logger_exception, pamhyr_db_need_update from tools import logger_exception, pamhyr_db_need_update, timer
from PyQt5 import QtGui from PyQt5 import QtGui
from PyQt5.QtGui import ( from PyQt5.QtGui import (
@ -84,10 +84,12 @@ from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import ( from View.RunSolver.Window import (
SelectSolverWindow, SelectSolverWindow,
SolverLogWindow, SolverLogWindow,
CompareSolversWindow
) )
from View.Results.Window import ResultsWindow from View.Results.Window import ResultsWindow
from View.Results.CompareDialog import (
CompareSolversWindow, CompareScenariosWindow
)
from View.RunSolver.WindowAdisTS import ( from View.RunSolver.WindowAdisTS import (
SelectSolverWindowAdisTS, SelectSolverWindowAdisTS,
@ -128,8 +130,8 @@ no_model_action = [
model_action = [ model_action = [
"action_menu_close", "action_menu_edit", "action_menu_save", "action_menu_close", "action_menu_edit", "action_menu_save",
"action_menu_save_as", "action_toolBar_close", "action_toolBar_save", "action_menu_save_as", "action_toolBar_close", "action_toolBar_save",
"action_menu_numerical_parameter", "action_open_results_from_file", "action_menu_numerical_parameter", "action_menu_open_results_from_file",
"action_open_results_adists", "action_menu_open_results_adists",
] ]
other_model_action = [ other_model_action = [
@ -150,8 +152,10 @@ define_model_action = [
"action_menu_run_solver", "action_menu_sediment_layers", "action_menu_run_solver", "action_menu_sediment_layers",
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs", "action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs",
"action_menu_edit_hydraulic_structures", "action_menu_additional_file", "action_menu_edit_hydraulic_structures", "action_menu_additional_file",
"action_menu_results_last", "action_open_results_from_file", "action_menu_results_last", "action_menu_open_results_from_file",
"action_compare_results", "action_menu_boundary_conditions_sediment", # "action_menu_compare_results",
"action_menu_compare_scenarios_results",
"action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_rk", "action_menu_rep_additional_lines", "action_menu_output_rk",
"action_menu_run_adists", "action_menu_pollutants", "action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90", "action_menu_dif", "action_menu_d90", "action_menu_dif",
@ -296,9 +300,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_rep_additional_lines": self.open_rep_lines, "action_menu_rep_additional_lines": self.open_rep_lines,
"action_menu_close": self.close_model, "action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results, "action_menu_results_last": self.open_last_results,
"action_open_results_from_file": self.open_results_from_file, "action_menu_open_results_from_file": self.open_results_from_file,
"action_compare_results": self.compare_results, # "action_menu_compare_results": self.compare_results,
"action_open_results_adists": self.open_results_adists, "action_menu_compare_scenarios_results":
self.compare_results_scenarios,
"action_menu_open_results_adists": self.open_results_adists,
# Help # Help
"action_menu_pamhyr_users_wiki": self.open_doc_user, "action_menu_pamhyr_users_wiki": self.open_doc_user,
"action_menu_pamhyr_developers_pdf": "action_menu_pamhyr_developers_pdf":
@ -596,7 +602,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if self._study is None: if self._study is None:
return None return None
return self._study.results results = self._study.results
if self._last_solver._type in results:
return self._study.results[self._last_solver._type]
return None
@last_results.setter @last_results.setter
def last_results(self, results): def last_results(self, results):
@ -1507,7 +1517,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return False return False
def open_solver_results(self, solver, results=None): def open_solver_results(self, solver, results=None):
logger.info(f"{solver} {results}") logger.info(f"Open results for '{solver}' ({results})")
def reading_fn(): def reading_fn():
self._tmp_results = results self._tmp_results = results
@ -1637,12 +1647,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
res.show() res.show()
def _solver_workdir(self, solver): def _solver_workdir(self, solver, scenario=None):
if scenario is None:
scenario = self._study.status.scenario
workdir = os.path.join( workdir = os.path.join(
os.path.dirname(self._study.filename), os.path.dirname(self._study.filename),
"_PAMHYR_", "_PAMHYR_",
self._study.name.replace(" ", "_"), self._study.name.replace(" ", "_"),
solver.name.replace(" ", "_"), solver.name.replace(" ", "_"),
scenario.workdir(),
) )
return workdir return workdir
@ -1697,32 +1711,73 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
results=file_name[0] results=file_name[0]
) )
def compare_results(self): # def compare_results(self):
# if self._study is None:
# return
# run = CompareSolversWindow(
# study=self._study,
# config=self.conf,
# parent=self
# )
# if not run.exec():
# return
# results = self.diff_results(
# run.solver1, run.solver2
# )
# # At least one result not available
# if results is None:
# return
# # Windows already opened
# if self.sub_window_exists(
# CompareSolversWindow,
# data=[self._study, None] +
# [r._solver for r in results] +
# [r._repertory for r in results] +
# [r._name for r in results]
# ):
# return
# res = ResultsWindow(
# study=self._study,
# results=results,
# parent=self
# )
# res.show()
def compare_results_scenarios(self):
if self._study is None: if self._study is None:
return return
run = CompareSolversWindow( dlg = CompareScenariosWindow(
study=self._study, study=self._study,
config=self.conf, config=self.conf,
parent=self parent=self
) )
if run.exec(): if not dlg.exec():
results = self.diff_results(run.solver1,
run.solver2)
else:
return return
results = self.diff_results(
dlg.solver1, dlg.solver2,
scenario1=dlg.scenario1,
scenario2=dlg.scenario2
)
# At least one result not available # At least one result not available
if results is None: if results is None:
return return
# Windows already opened # Windows already opened
if self.sub_window_exists( if self.sub_window_exists(
ResultsWindow, CompareScenariosWindow,
data=[self._study, None] + data=[self._study, None] +
[r._solver for r in results] + [r._solver for r in results] +
[r._repertory for r in results] + [r._repertory for r in results] +
[r._name for r in results] [r._name for r in results] +
[dlg.scenario1, dlg.scenario2]
): ):
return return
@ -1733,97 +1788,109 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
res.show() res.show()
def diff_results(self, solver1, solver2): def msg_diff_results_param(self):
if solver1 is None: self.message_box(
# TODO message window_title=self._trad["Error"],
return None text=self._trad["mb_diff_results_title"],
if solver2 is None: informative_text=self._trad["mb_diff_results_param_msg"]
# TODO message )
def msg_diff_results_compatibility(self):
self.message_box(
window_title=self._trad["Error"],
text=self._trad["mb_diff_results_title"],
informative_text=self._trad["mb_diff_results_compatibility_msg"]
)
@timer
def diff_results(self, solver1, solver2,
scenario1=None, scenario2=None):
if solver1 is None or solver2 is None:
self.msg_diff_results_param()
return None return None
solver3 = GenericSolver(solver1.name+" - "+solver2.name) study1 = self._study
solver4 = GenericSolver(solver1.name) study2 = self._study
solver5 = GenericSolver(solver2.name)
s3_name = solver1.name + " <> " + solver2.name
if not (scenario2 is None or scenario2 is None):
study1 = self._study.copy_from_scenario(scenario1)
study2 = self._study.copy_from_scenario(scenario2)
s3_name = scenario1.name + " <> " + scenario2.name
solver3 = GenericSolver(s3_name)
result1 = solver1.results( result1 = solver1.results(
self._study, study1,
self._solver_workdir(solver1), self._solver_workdir(solver1, scenario=scenario1),
) )
if result1 is None:
# TODO message
return None
result2 = solver2.results( result2 = solver2.results(
self._study, study2,
self._solver_workdir(solver2), self._solver_workdir(solver2, scenario=scenario2),
) )
if result2 is None: if result1 is None or result2 is None:
# TODO message self.msg_diff_results_param()
return None return None
if result2.get("nb_reach") != result1.get("nb_reach"): if result1.get("nb_reach") != result2.get("nb_reach"):
# TODO message self.msg_diff_results_compatibility()
return None return None
if result2.get("nb_profile") != result1.get("nb_profile"): if result1.get("nb_profile") != result2.get("nb_profile"):
# TODO message self.msg_diff_results_compatibility()
return None return None
# return [result1, result2] return self._diff_results(
solver1, solver2, solver3,
result1, result2,
)
@timer
def _diff_results(self, solver1, solver2, solver3, result1, result2):
result3 = Results(study=self._study, solver=solver3)
ts = sorted(
list(
result1.get("timestamps").intersection(
result2.get("timestamps")
)
)
)
result3 = Results(self._study, solver3)
result4 = Results(self._study, solver1)
result5 = Results(self._study, solver2)
result3.set("nb_reach", result1.get("nb_reach")) result3.set("nb_reach", result1.get("nb_reach"))
result4.set("nb_reach", result1.get("nb_reach"))
result5.set("nb_reach", result1.get("nb_reach"))
result3.set("nb_profile", result1.get("nb_profile")) result3.set("nb_profile", result1.get("nb_profile"))
result4.set("nb_profile", result1.get("nb_profile"))
result5.set("nb_profile", result1.get("nb_profile"))
ts = sorted(list(result1.get("timestamps").intersection(
result2.get("timestamps"))))
result3.set("timestamps", ts) result3.set("timestamps", ts)
result4.set("timestamps", ts)
result5.set("timestamps", ts)
for i in range(int(result1.get("nb_reach"))): for i in range(int(result1.get("nb_reach"))):
# Add reach to results reach list
r = result3.river.add(i) r = result3.river.add(i)
r = result4.river.add(i)
r = result5.river.add(i)
for timestamp in result3.get("timestamps"): for timestamp in result3.get("timestamps"):
for r in range(int(result1.get("nb_reach"))): for r in range(int(result1.get("nb_reach"))):
reach1 = result1.river.reach(r) reach1 = result1.river.reach(r)
reach2 = result2.river.reach(r) reach2 = result2.river.reach(r)
reach3 = result3.river.reach(r) reach3 = result3.river.reach(r)
reach4 = result4.river.reach(r)
reach5 = result5.river.reach(r) for profile1, profile2, profile3 in zip(
for p, (profile1, profile2) in enumerate(zip( reach1.profiles,
reach1.profiles, reach2.profiles)): reach2.profiles,
reach3.profiles):
for key in ["Z", "Q", "V"]: for key in ["Z", "Q", "V"]:
d1 = profile1.get_ts_key(timestamp, key) d1 = profile1.get_ts_key(timestamp, key)
d2 = profile2.get_ts_key(timestamp, key) d2 = profile2.get_ts_key(timestamp, key)
d = d1-d2 d = d1 - d2
reach3.set(p, timestamp, key, d)
reach4.set(p, timestamp, key, d1)
reach5.set(p, timestamp, key, d2)
limits = reach3.profile(p).geometry.get_water_limits(
reach3.profile(p).get_ts_key(timestamp, "Z")
)
reach3.set(
p, timestamp,
"water_limits",
limits
)
limits = profile1.get_ts_key(timestamp, "water_limits")
reach4.set(p, timestamp, "water_limits", limits)
limits = profile2.get_ts_key(timestamp, "water_limits")
reach5.set(p, timestamp, "water_limits", limits)
return [result4, result5, result3] profile3.set(timestamp, key, d)
limits = profile3.geometry\
.get_water_limits(
profile3.get_ts_key(timestamp, "Z")
)
profile3.set(timestamp, "water_limits", limits)
return [result1, result2, result3]
def open_results_adists(self): def open_results_adists(self):
if self._study is None: if self._study is None:
@ -1866,6 +1933,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
solver_results_adists = solver_results.results( solver_results_adists = solver_results.results(
self._study, self._study,
repertory=dir_path, qlog=None) # self._output) repertory=dir_path, qlog=None) # self._output)
logger.info(f"Select results: {dir_path}") logger.info(f"Select results: {dir_path}")
if len(bin_list) >= 2 and ("total_sediment.bin" in bin_list): if len(bin_list) >= 2 and ("total_sediment.bin" in bin_list):
self.open_solver_results_adists( self.open_solver_results_adists(

View File

@ -0,0 +1,252 @@
import os
import logging
import tempfile
from queue import Queue
from tools import trace, timer, logger_exception
from View.Tools.PamhyrWindow import PamhyrDialog, PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
)
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect, QTimer, QProcess,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QTextEdit,
)
from View.WaitingDialog import WaitingDialog
_translate = QCoreApplication.translate
logger = logging.getLogger()
class CompareSolversWindow(PamhyrDialog):
_pamhyr_ui = "CompareSolvers"
_pamhyr_name = "Compare solvers"
def __init__(self, study=None, config=None,
parent=None):
self._solver1 = None
self._solver2 = None
name = _translate("Solver", "Compare solvers")
super(CompareSolversWindow, self).__init__(
title=name,
study=study,
config=config,
options=[],
parent=parent
)
self.setup_solvers()
self.setup_connections()
self.select_last_solver()
def setup_solvers(self):
solvers = self._config.solvers
solvers_name = list(
map(
self._format_solver_name,
solvers
)
)
self.combobox_add_items("comboBox1", solvers_name)
self.combobox_add_items("comboBox2", solvers_name)
def setup_connections(self):
self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.reject)
def select_last_solver(self):
solvers = self._config.solvers
last = self._config.last_solver_name
solver = list(
filter(
lambda s: s.name == last,
solvers
)
)
if len(solver) != 0:
self.set_combobox_text(
"comboBox1",
self._format_solver_name(solver[0])
)
def _format_solver_name(self, solver):
return f"{solver.name} - ({solver._type})"
@property
def solver1(self):
return self._solver1
@property
def solver2(self):
return self._solver2
def accept(self):
solver_name1 = self.get_combobox_text("comboBox1")
solver_name1 = solver_name1.rsplit(" - ", 1)[0]
self._solver1 = next(
filter(
lambda s: s.name == solver_name1,
self._config.solvers
)
)
solver_name2 = self.get_combobox_text("comboBox2")
solver_name2 = solver_name2.rsplit(" - ", 1)[0]
self._solver2 = next(
filter(
lambda s: s.name == solver_name2,
self._config.solvers
)
)
super(CompareSolversWindow, self).accept()
class CompareScenariosWindow(PamhyrDialog):
_pamhyr_ui = "CompareScenarios"
_pamhyr_name = "Compare scenarios"
def __init__(self, study=None, config=None,
parent=None):
self._solver1 = None
self._solver2 = None
self._scenario1 = None
self._scenario2 = None
name = _translate("Solver", "Compare solvers")
super(CompareScenariosWindow, self).__init__(
title=name,
study=study,
config=config,
options=[],
parent=parent
)
self.setup_solvers()
self.setup_scenarios()
self.setup_connections()
self.select_last_solver()
def setup_solvers(self):
solvers = self._config.solvers
solvers_name = list(
map(
self._format_solver_name, solvers
)
)
self.combobox_add_items("comboBoxSolver1", solvers_name)
self.combobox_add_items("comboBoxSolver2", solvers_name)
def setup_scenarios(self):
scenarios = self._study.scenarios.lst
scenarios_name = list(
map(
lambda s: s.name, scenarios
)
)
self.combobox_add_items("comboBoxScenario1", scenarios_name)
self.combobox_add_items("comboBoxScenario2", scenarios_name)
def setup_connections(self):
self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.reject)
def select_last_solver(self):
solvers = self._config.solvers
last = self._config.last_solver_name
solver = list(
filter(
lambda s: s.name == last,
solvers
)
)
if len(solver) != 0:
self.set_combobox_text(
"comboBoxSolver1",
self._format_solver_name(solver[0])
)
self.set_combobox_text(
"comboBoxSolver2",
self._format_solver_name(solver[0])
)
def _format_solver_name(self, solver):
return f"{solver.name} - ({solver._type})"
@property
def solver1(self):
return self._solver1
@property
def solver2(self):
return self._solver2
@property
def scenario1(self):
return self._scenario1
@property
def scenario2(self):
return self._scenario2
def accept(self):
solver_name1 = self.get_combobox_text("comboBoxSolver1")
solver_name1 = solver_name1.rsplit(" - ", 1)[0]
self._solver1 = next(
filter(
lambda s: s.name == solver_name1,
self._config.solvers
)
)
solver_name2 = self.get_combobox_text("comboBoxSolver2")
solver_name2 = solver_name2.rsplit(" - ", 1)[0]
self._solver2 = next(
filter(
lambda s: s.name == solver_name2,
self._config.solvers
)
)
scenario_name1 = self.get_combobox_text("comboBoxScenario1")
self._scenario1 = next(
filter(
lambda s: s.name == scenario_name1,
self._study.scenarios
)
)
scenario_name2 = self.get_combobox_text("comboBoxScenario2")
self._scenario2 = next(
filter(
lambda s: s.name == scenario_name2,
self._study.scenarios
)
)
super(CompareScenariosWindow, self).accept()

View File

@ -138,107 +138,6 @@ class SelectSolverWindow(PamhyrDialog):
super(SelectSolverWindow, self).accept() super(SelectSolverWindow, self).accept()
class CompareSolversWindow(PamhyrDialog):
_pamhyr_ui = "CompareSolvers"
_pamhyr_name = "Compare solvers"
def __init__(self, study=None, config=None,
parent=None):
self._solver1 = None
self._solver2 = None
name = _translate("Solver", "Compare solvers")
super(CompareSolversWindow, self).__init__(
title=name,
study=study,
config=config,
options=[],
parent=parent
)
self.setup_combobox1()
self.setup_combobox2()
self.setup_connections()
self.select_last_solver()
def setup_combobox1(self):
solvers = self._config.solvers
solvers_name = list(
map(
self._format_solver_name,
solvers
)
)
self.combobox_add_items("comboBox1", solvers_name)
def setup_combobox2(self):
solvers = self._config.solvers
solvers_name = list(
map(
self._format_solver_name,
solvers
)
)
self.combobox_add_items("comboBox2", solvers_name)
def setup_connections(self):
self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.reject)
def select_last_solver(self):
solvers = self._config.solvers
last = self._config.last_solver_name
solver = list(
filter(
lambda s: s.name == last,
solvers
)
)
if len(solver) != 0:
self.set_combobox_text(
"comboBox1",
self._format_solver_name(solver[0])
)
def _format_solver_name(self, solver):
return f"{solver.name} - ({solver._type})"
@property
def solver1(self):
return self._solver1
@property
def solver2(self):
return self._solver2
def accept(self):
solver_name1 = self.get_combobox_text("comboBox1")
solver_name1 = solver_name1.rsplit(" - ", 1)[0]
self._solver1 = next(
filter(
lambda s: s.name == solver_name1,
self._config.solvers
)
)
solver_name2 = self.get_combobox_text("comboBox2")
solver_name2 = solver_name2.rsplit(" - ", 1)[0]
self._solver2 = next(
filter(
lambda s: s.name == solver_name2,
self._config.solvers
)
)
super(CompareSolversWindow, self).accept()
class SolverLogWindow(PamhyrWindow): class SolverLogWindow(PamhyrWindow):
_pamhyr_ui = "SolverLog" _pamhyr_ui = "SolverLog"
_pamhyr_name = "Solver Log" _pamhyr_name = "Solver Log"

View File

@ -228,6 +228,18 @@ class MainTranslate(UnitTranslate):
"Do you still want to open those results?" "Do you still want to open those results?"
) )
self._dict["mb_diff_results_title"] = _translate(
"MainWindow", "Results compare"
)
self._dict["mb_diff_results_param_msg"] = _translate(
"MainWindow", "Results comparison parameters is invalid"
)
self._dict["mb_diff_results_compatibility_msg"] = _translate(
"MainWindow",
"Results comparison with two "
"incompatible study version"
)
self._dict["x"] = _translate("MainWindow", "X (m)") self._dict["x"] = _translate("MainWindow", "X (m)")
self._dict["y"] = _translate("MainWindow", "Y (m)") self._dict["y"] = _translate("MainWindow", "Y (m)")
self._dict["Yes"] = _translate("MainWindow", "Yes") self._dict["Yes"] = _translate("MainWindow", "Yes")

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>117</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="locale">
<locale language="English" country="Europe"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="comboBoxSolver1"/>
</item>
<item>
<widget class="QComboBox" name="comboBoxSolver2"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QComboBox" name="comboBoxScenario1"/>
</item>
<item>
<widget class="QComboBox" name="comboBoxScenario2"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_ok">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -160,9 +160,9 @@
<string>&amp;Results</string> <string>&amp;Results</string>
</property> </property>
<addaction name="action_menu_results_last"/> <addaction name="action_menu_results_last"/>
<addaction name="action_open_results_from_file"/> <addaction name="action_menu_open_results_from_file"/>
<addaction name="action_compare_results"/> <addaction name="action_menu_compare_scenarios_results"/>
<addaction name="action_open_results_adists"/> <addaction name="action_menu_open_results_adists"/>
</widget> </widget>
<widget class="QMenu" name="menu_help"> <widget class="QMenu" name="menu_help">
<property name="title"> <property name="title">
@ -726,7 +726,7 @@
<string>Edit hydraulic structures</string> <string>Edit hydraulic structures</string>
</property> </property>
</action> </action>
<action name="action_open_results_from_file"> <action name="action_menu_open_results_from_file">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -787,12 +787,15 @@
<string>DIF</string> <string>DIF</string>
</property> </property>
</action> </action>
<action name="action_compare_results"> <action name="action_menu_compare_results">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Compare results</string> <string>Compare results</string>
</property> </property>
</action> </action>
<action name="action_open_results_adists"> <action name="action_menu_open_results_adists">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -800,6 +803,14 @@
<string>Open results AdisTS</string> <string>Open results AdisTS</string>
</property> </property>
</action> </action>
<action name="action_menu_compare_scenarios_results">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Compare results</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections> <connections>