Merge branch 'v0.0.8' of gitlab-ssh.irstea.fr:theophile.terraz/pamhyr into v0.0.8

setup.py
Theophile Terraz 2024-05-16 13:01:14 +02:00
commit b784cab94b
16 changed files with 345 additions and 36 deletions

View File

@ -298,7 +298,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
lplan: bool = False, lplan: bool = False,
lm: int = 3, lm: int = 3,
linear: bool = False, linear: bool = False,
origin_value = 0.0): origin_value=0.0):
if reach is None or len(reach.profiles) == 0: if reach is None or len(reach.profiles) == 0:
return reach return reach
@ -371,7 +371,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
lplan: bool = False, lplan: bool = False,
lm: int = 3, lm: int = 3,
linear: bool = False, linear: bool = False,
origin_value = 0.0): origin_value=0.0):
if reach is None or len(reach.profiles) == 0: if reach is None or len(reach.profiles) == 0:
return reach return reach

View File

@ -583,27 +583,44 @@ class ProfileXYZ(Profile, SQLSubModel):
""" """
Remove points to keep at most np_purge points. Remove points to keep at most np_purge points.
""" """
if (self.nb_points <= np_purge):
return
if (self.nb_points <= np_purge): return nb_named = 2 # we consider the first and last point as named
nb_named = 2 # we consider the first and last point as named
area = [0.0] area = [0.0]
for i in range(1, self.nb_points-1): for i in range(1, self.nb_points-1):
if self.point(i).point_is_named(): if self.point(i).point_is_named():
area.append(9999999.999) area.append(9999999.999)
nb_named += 1 nb_named += 1
else: else:
area.append(PointXYZ.areatriangle3d(self.point(i-1),self.point(i),self.point(i+1))) area.append(
PointXYZ.areatriangle3d(
self.point(i-1),
self.point(i),
self.point(i+1))
)
area.append(0.0) area.append(0.0)
while (self.nb_points > max(np_purge, nb_named)): while self.nb_points > max(np_purge, nb_named):
to_rm = np.argmin(area[1:self.nb_points-1])+1 to_rm = np.argmin(area[1:self.nb_points - 1]) + 1
self.delete_i([to_rm]) self.delete_i([to_rm])
area.pop(to_rm) area.pop(to_rm)
for i in [to_rm-1, to_rm]: for i in [to_rm-1, to_rm]:
if (i == 0): continue if (i == 0):
if (i == self.nb_points - 1): continue continue
if (i == self.nb_points - 1):
continue
if self.point(i).point_is_named(): if self.point(i).point_is_named():
area[i] = 9999999.999 area[i] = 9999999.999
else: else:
area[i] = PointXYZ.areatriangle3d(self.point(i-1),self.point(i),self.point(i+1)) area[i] = PointXYZ.areatriangle3d(
self.point(i-1),
self.point(i),
self.point(i+1)
)

View File

@ -266,6 +266,9 @@ class InitialConditions(SQLSubModel):
def __len__(self): def __len__(self):
return len(self._data) return len(self._data)
def lst(self):
return self._data
@property @property
def reach(self): def reach(self):
return self._reach return self._reach
@ -295,6 +298,15 @@ class InitialConditions(SQLSubModel):
self._data.insert(index, n) self._data.insert(index, n)
self._status.modified() self._status.modified()
def new_from_data(self, kp, discharge, elevation):
n = Data(reach=self._reach, status=self._status)
n['kp'] = kp
n['discharge'] = discharge
n['elevation'] = elevation
return n
def insert(self, index, data): def insert(self, index, data):
self._data.insert(index, data) self._data.insert(index, data)
self._status.modified() self._status.modified()

View File

@ -126,3 +126,11 @@ class River(object):
self._reachs.append(new) self._reachs.append(new)
return new return new
def get_reach_by_geometry(self, geometry_reach):
return next(
filter(
lambda r: r.geometry is geometry_reach,
self._reachs
)
)

View File

@ -25,8 +25,11 @@ logger = logging.getLogger()
class PlotKPZ(PamhyrPlot): class PlotKPZ(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, toolbar=None, def __init__(self, canvas=None, trad=None,
study=None, data=None, toolbar=None,
parent=None): parent=None):
self._study = study
super(PlotKPZ, self).__init__( super(PlotKPZ, self).__init__(
canvas=canvas, canvas=canvas,
trad=trad, trad=trad,
@ -75,6 +78,7 @@ class PlotKPZ(PamhyrPlot):
self.draw_current() self.draw_current()
self.draw_gl() self.draw_gl()
self.draw_bottom() self.draw_bottom()
self.draw_profiles_hs(self._data)
self.idle() self.idle()
self._init = True self._init = True
@ -184,6 +188,34 @@ class PlotKPZ(PamhyrPlot):
color='lightgrey' color='lightgrey'
) )
def draw_profiles_hs(self, reach):
lhs = filter(
lambda hs: hs._input_reach.reach is reach,
self._study.river.hydraulic_structures.lst
)
for hs in lhs:
x = hs.input_kp
z_min = reach.get_z_min()
z_max = reach.get_z_max()
self.canvas.axes.plot(
[x, x],
[min(z_min), max(z_max)],
linestyle="--",
lw=1.,
color=self.color_plot_previous,
)
self.canvas.axes.annotate(
" > " + hs.name,
(x, max(z_max)),
horizontalalignment='left',
verticalalignment='top',
annotation_clip=True,
fontsize=9, color=self.color_plot_previous,
)
@timer @timer
def update(self): def update(self):
if not self._init: if not self._init:

View File

@ -74,8 +74,10 @@ class UpdateKPDialog(PamhyrDialog):
def changed_profile(self): def changed_profile(self):
origin = self.get_combobox_text("comboBox_origin") origin = self.get_combobox_text("comboBox_origin")
self.set_double_spin_box("doubleSpinBox_origin", self.set_double_spin_box(
self._reach.profile(self.profiles.index(origin)).kp) "doubleSpinBox_origin",
self._reach.profile(self.profiles.index(origin)).kp
)
@property @property
def profiles(self): def profiles(self):

View File

@ -375,6 +375,7 @@ class GeometryWindow(PamhyrWindow):
self._plot_kpc = PlotKPZ( self._plot_kpc = PlotKPZ(
canvas=self._canvas_kpc, canvas=self._canvas_kpc,
study=self._study,
data=self._reach, data=self._reach,
trad=self._trad, trad=self._trad,
toolbar=self._toolbar_kpc toolbar=self._toolbar_kpc

View File

@ -37,7 +37,7 @@ from View.Tools.PamhyrTable import PamhyrTableModel
from View.InitialConditions.UndoCommand import ( from View.InitialConditions.UndoCommand import (
SetCommand, AddCommand, DelCommand, SetCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand, SortCommand, MoveCommand, InsertCommand,
DuplicateCommand, GenerateCommand, DuplicateCommand, GenerateCommand,
) )
@ -197,7 +197,7 @@ class InitialConditionTableModel(PamhyrTableModel):
self.endMoveRows() self.endMoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()): def move_down(self, row, parent=QModelIndex()):
if row > len(self._lst): if row > len(self._lst):
return return
@ -214,6 +214,73 @@ class InitialConditionTableModel(PamhyrTableModel):
self.endMoveRows() self.endMoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def paste(self, index, header, data):
if len(header) != 0:
logger.error("Unexpected header in IC past data")
return
if len(data) == 0:
logger.error("Empty data")
return
if len(data[0]) != 3:
logger.error(f"Unexpected data size: [{data[0]}, ...]")
return
self.layoutAboutToBeChanged.emit()
self._undo.push(
InsertCommand(
self._lst, index,
list(
map(
lambda d: self._lst.new_from_data(*d),
data
)
)
)
)
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def import_from_results(self, index, results):
if results is None:
logger.error("No results data")
return
self.layoutAboutToBeChanged.emit()
ts = max(results.get("timestamps"))
res_reach = results.river.get_reach_by_geometry(
self._reach.reach
)
data = list(
map(
lambda p: [
p.geometry.kp,
p.get_ts_key(ts, "Q"),
p.get_ts_key(ts, "Z"),
],
res_reach.profiles
)
)
self._undo.push(
InsertCommand(
self._lst, index,
list(
map(
lambda d: self._lst.new_from_data(*d),
data
)
)
)
)
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def undo(self): def undo(self):
self._undo.undo() self._undo.undo()
self.layoutChanged.emit() self.layoutChanged.emit()

View File

@ -139,7 +139,7 @@ class MoveCommand(QUndoCommand):
self._ics.move_down(self._i) self._ics.move_down(self._i)
class PasteCommand(QUndoCommand): class InsertCommand(QUndoCommand):
def __init__(self, ics, row, ic): def __init__(self, ics, row, ic):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)

View File

@ -16,9 +16,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os
import logging import logging
from tools import trace, timer from tools import trace, timer, logger_exception
from View.Tools.PamhyrWindow import PamhyrWindow from View.Tools.PamhyrWindow import PamhyrWindow
@ -43,7 +44,7 @@ from Modules import Modules
from View.InitialConditions.UndoCommand import ( from View.InitialConditions.UndoCommand import (
SetCommand, AddCommand, DelCommand, SetCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand, SortCommand, MoveCommand, InsertCommand,
DuplicateCommand, DuplicateCommand,
) )
@ -59,6 +60,9 @@ from View.InitialConditions.PlotDischarge import PlotDischarge
from View.InitialConditions.translate import ICTranslate from View.InitialConditions.translate import ICTranslate
from View.InitialConditions.DialogHeight import HeightDialog from View.InitialConditions.DialogHeight import HeightDialog
from View.InitialConditions.DialogDischarge import DischargeDialog from View.InitialConditions.DialogDischarge import DischargeDialog
from View.Results.ReadingResultsDialog import ReadingResultsDialog
from Solver.Mage import Mage8
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
@ -166,6 +170,8 @@ class InitialConditionsWindow(PamhyrWindow):
self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort) self.find(QAction, "action_sort").triggered.connect(self.sort)
self.find(QAction, "action_import").triggered\
.connect(self.import_from_file)
self.find(QPushButton, "pushButton_generate_1").clicked.connect( self.find(QPushButton, "pushButton_generate_1").clicked.connect(
self.generate_growing_constante_height self.generate_growing_constante_height
@ -179,9 +185,13 @@ class InitialConditionsWindow(PamhyrWindow):
def index_selected_row(self): def index_selected_row(self):
table = self.find(QTableView, f"tableView") table = self.find(QTableView, f"tableView")
return table.selectionModel()\ rows = table.selectionModel()\
.selectedRows()[0]\ .selectedRows()
.row()
if len(rows) == 0:
return 0
return rows[0].row()
def update(self): def update(self):
self._update_plot() self._update_plot()
@ -230,6 +240,42 @@ class InitialConditionsWindow(PamhyrWindow):
self._table.sort(False) self._table.sort(False)
self._update() self._update()
def import_from_file(self):
workdir = os.path.dirname(self._study.filename)
return self.file_dialog(
callback=lambda d: self._import_from_file(d[0]),
directory=workdir,
default_suffix=".BIN",
file_filter=["Mage (*.BIN)"],
)
def _import_from_file(self, file_name):
solver = Mage8("dummy")
name = os.path.basename(file_name)\
.replace(".BIN", "")
def reading():
self._tmp_results = solver.results(
self._study,
os.path.dirname(file_name),
name=name
)
dlg = ReadingResultsDialog(
reading_fn=reading,
parent=self
)
dlg.exec_()
results = self._tmp_results
self._import_from_results(results)
def _import_from_results(self, results):
logger.debug(f"import from results: {results}")
row = self.index_selected_row()
self._table.import_from_results(row, results)
def move_up(self): def move_up(self):
row = self.index_selected_row() row = self.index_selected_row()
self._table.move_up(row) self._table.move_up(row)
@ -241,11 +287,49 @@ class InitialConditionsWindow(PamhyrWindow):
self._update() self._update()
def _copy(self): def _copy(self):
logger.info("TODO: copy") rows = list(
self._update() map(
lambda row: row.row(),
self.tableView.selectionModel().selectedRows()
)
)
table = list(
map(
lambda eic: list(
map(
lambda k: eic[1][k],
["kp", "discharge", "elevation"]
)
),
filter(
lambda eic: eic[0] in rows,
enumerate(self._ics.lst())
)
)
)
self.copyTableIntoClipboard(table)
def _paste(self): def _paste(self):
logger.info("TODO: paste") header, data = self.parseClipboardTable()
if len(data) + len(header) == 0:
return
logger.debug(
"IC: Paste: " +
f"header = {header}, " +
f"data = {data}"
)
try:
row = self.index_selected_row()
# self._table.paste(row, header, data)
self._table.paste(row, [], data)
except Exception as e:
logger_exception(e)
self._update() self._update()
def _undo(self): def _undo(self):

View File

@ -63,6 +63,14 @@ class ComboBoxDelegate(QItemDelegate):
self._tab = tab self._tab = tab
self._trad = trad self._trad = trad
@property
def data(self):
return self._data
@data.setter
def data(self, data):
self._data = data
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
self.editor = QComboBox(parent) self.editor = QComboBox(parent)
long_types = self._trad.get_dict("long_types") long_types = self._trad.get_dict("long_types")
@ -78,6 +86,17 @@ class ComboBoxDelegate(QItemDelegate):
) )
) )
self.editor.addItems(lst) self.editor.addItems(lst)
elif self._mode == "kp":
if self._data is None:
self.editor.addItems(
["0"]
)
else:
self.editor.addItems(
list(
map(str, self._data.reach.get_kp())
)
)
else: else:
self.editor.addItems( self.editor.addItems(
[self._trad['not_associated']] + [self._trad['not_associated']] +
@ -136,9 +155,9 @@ class TableModel(PamhyrTableModel):
return self._trad['not_associated'] return self._trad['not_associated']
return n.name return n.name
elif self._headers[column] == "begin_kp": elif self._headers[column] == "begin_kp":
return self._lst.get(self._tab, row).begin_kp return str(self._lst.get(self._tab, row).begin_kp)
elif self._headers[column] == "end_kp": elif self._headers[column] == "end_kp":
return self._lst.get(self._tab, row).end_kp return str(self._lst.get(self._tab, row).end_kp)
return QVariant() return QVariant()

View File

@ -88,6 +88,8 @@ class LateralContributionWindow(PamhyrWindow):
def setup_table(self): def setup_table(self):
self._table = {} self._table = {}
self._delegate_kp = []
for t in ["liquid", "solid", "suspenssion"]: for t in ["liquid", "solid", "suspenssion"]:
self._delegate_type = ComboBoxDelegate( self._delegate_type = ComboBoxDelegate(
data=self._study.river, data=self._study.river,
@ -96,6 +98,16 @@ class LateralContributionWindow(PamhyrWindow):
trad=self._trad, trad=self._trad,
parent=self parent=self
) )
delegate_kp = ComboBoxDelegate(
data=None,
mode="kp",
tab=t,
trad=self._trad,
parent=self
)
self._delegate_kp.append(delegate_kp)
self._delegate_edge = ComboBoxDelegate( self._delegate_edge = ComboBoxDelegate(
data=self._study.river, data=self._study.river,
mode="edge", mode="edge",
@ -112,6 +124,8 @@ class LateralContributionWindow(PamhyrWindow):
delegates={ delegates={
"type": self._delegate_type, "type": self._delegate_type,
"edge": self._delegate_edge, "edge": self._delegate_edge,
"begin_kp": delegate_kp,
"end_kp": delegate_kp,
}, },
data=self._study.river, data=self._study.river,
undo=self._undo_stack, undo=self._undo_stack,
@ -185,8 +199,7 @@ class LateralContributionWindow(PamhyrWindow):
highlight = None highlight = None
if len(rows) > 0: if len(rows) > 0:
edge = self._study\ edge = self._study.river\
.river\
.lateral_contribution\ .lateral_contribution\
.get(tab, rows[0])\ .get(tab, rows[0])\
.edge .edge
@ -195,6 +208,9 @@ class LateralContributionWindow(PamhyrWindow):
lc = self._lcs.get(tab, rows[0]) lc = self._lcs.get(tab, rows[0])
highlight = (lc.begin_kp, lc.end_kp) highlight = (lc.begin_kp, lc.end_kp)
for delegate in self._delegate_kp:
delegate.data = edge
self.plot = PlotXY( self.plot = PlotXY(
canvas=self.canvas, canvas=self.canvas,
data=data, data=data,

View File

@ -79,6 +79,7 @@ class PlotKPC(PamhyrPlot):
self.draw_water_elevation_max(reach) self.draw_water_elevation_max(reach)
self.draw_water_elevation_overflow(reach) self.draw_water_elevation_overflow(reach)
self.draw_current(reach) self.draw_current(reach)
self.draw_profiles_hs(reach)
# self.enable_legend() # self.enable_legend()
@ -105,6 +106,34 @@ class PlotKPC(PamhyrPlot):
self._river_bottom = z self._river_bottom = z
def draw_profiles_hs(self, reach):
lhs = filter(
lambda hs: hs._input_reach.reach is reach.geometry,
self.results.study.river.hydraulic_structures.lst
)
for hs in lhs:
x = hs.input_kp
z_min = reach.geometry.get_z_min()
z_max = reach.geometry.get_z_max()
self.canvas.axes.plot(
[x, x],
[min(z_min), max(z_max)],
linestyle="--",
lw=1.,
color=self.color_plot_previous,
)
self.canvas.axes.annotate(
" > " + hs.name,
(x, max(z_max)),
horizontalalignment='left',
verticalalignment='top',
annotation_clip=True,
fontsize=9, color=self.color_plot_previous,
)
def sl_compute_bedrock(self, reach): def sl_compute_bedrock(self, reach):
z_min = reach.geometry.get_z_min() z_min = reach.geometry.get_z_min()
sl = self.sl_compute_initial(reach) sl = self.sl_compute_initial(reach)
@ -115,8 +144,8 @@ class PlotKPC(PamhyrPlot):
lambda z, h: z - h[0], lambda z, h: z - h[0],
sl, z sl, z
), ),
z_min, # Original geometry z_min, # Original geometry
sl # Original sediment layers sl # Original sediment layers
) )
) )
@ -132,8 +161,8 @@ class PlotKPC(PamhyrPlot):
lambda z, h: z + h[0], lambda z, h: z + h[0],
sl, z sl, z
), ),
z_br, # Bedrock elevation z_br, # Bedrock elevation
sl # Current sediment layers sl # Current sediment layers
) )
) )

View File

@ -87,7 +87,9 @@ class WindowToolKit(object):
def file_dialog(self, select_file=True, def file_dialog(self, select_file=True,
callback=lambda x: None, callback=lambda x: None,
directory=None): directory=None,
default_suffix=None,
file_filter=None):
"""Open a new file dialog and send result to callback function """Open a new file dialog and send result to callback function
Args: Args:
@ -95,7 +97,8 @@ class WindowToolKit(object):
callback: The callback function with one arguments, files callback: The callback function with one arguments, files
selection list selection list
directory: Defaut directory directory: Defaut directory
default_suffix: Default file suffix
file_filter: List of file filter
Returns: Returns:
The returns of callback The returns of callback
""" """
@ -110,6 +113,13 @@ class WindowToolKit(object):
if directory is not None: if directory is not None:
dialog.setDirectory(directory) dialog.setDirectory(directory)
if select_file:
if default_suffix is not None:
dialog.setDefaultSuffix(default_suffix)
if file_filter is not None:
dialog.setNameFilters(file_filter)
if dialog.exec_(): if dialog.exec_():
file_names = dialog.selectedFiles() file_names = dialog.selectedFiles()
return callback(file_names) return callback(file_names)

View File

@ -200,7 +200,6 @@ class PamhyrPlotToolbar(NavigationToolbar2QT):
self.icons.append(("save_figure", icon_save)) self.icons.append(("save_figure", icon_save))
def save_figure(self, *args): def save_figure(self, *args):
file_types = self.canvas.get_supported_filetypes_grouped() file_types = self.canvas.get_supported_filetypes_grouped()
default_file_type = self.canvas.get_default_filetype() default_file_type = self.canvas.get_default_filetype()

View File

@ -81,6 +81,7 @@
<attribute name="toolBarBreak"> <attribute name="toolBarBreak">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
<addaction name="action_import"/>
<addaction name="action_add"/> <addaction name="action_add"/>
<addaction name="action_del"/> <addaction name="action_del"/>
<addaction name="action_sort"/> <addaction name="action_sort"/>
@ -121,6 +122,18 @@
<string>Sort inital condition</string> <string>Sort inital condition</string>
</property> </property>
</action> </action>
<action name="action_import">
<property name="icon">
<iconset>
<normaloff>ressources/import.png</normaloff>ressources/import.png</iconset>
</property>
<property name="text">
<string>Import</string>
</property>
<property name="toolTip">
<string>Import from file</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>