mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
14
Commits
5f64362f65
...
9e5153d63d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e5153d63d | ||
|
|
797de09683 | ||
|
|
ef10588880 | ||
|
|
c2a8e17bf3 | ||
|
|
03979a8bea | ||
|
|
a4eee962f7 | ||
|
|
d8d1b4cd1f | ||
|
|
cdeca8a2e8 | ||
|
|
4d9c4cb5d0 | ||
|
|
618477b384 | ||
|
|
70a26d0036 | ||
|
|
c3afc4dd58 | ||
|
|
ef72e41b14 | ||
|
|
be7b5b892a |
No files matched your search
@@ -254,3 +254,46 @@ for each reach"
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
class MageLateralSolidChecker(AbstractModelChecker):
|
||||||
|
"""Mage has no solid lateral contribution (notice: only upstream
|
||||||
|
solid discharges, QSO): the solid ones are not exported (LAT holds
|
||||||
|
liquid discharges only). Non blocking warning"""
|
||||||
|
|
||||||
|
def __init__(self, version="mage8"):
|
||||||
|
super(MageLateralSolidChecker, self).__init__()
|
||||||
|
|
||||||
|
self._name = _translate(
|
||||||
|
"Checker", "Mage solid lateral contributions checker")
|
||||||
|
self._description = _translate(
|
||||||
|
"Checker",
|
||||||
|
"Check if the study has solid lateral contributions, which "
|
||||||
|
"Mage does not take into account"
|
||||||
|
)
|
||||||
|
self._solver = version
|
||||||
|
self._modules = Modules.LATERAL_CONTRIBUTION
|
||||||
|
|
||||||
|
def run(self, study):
|
||||||
|
if not self.basic_check(study):
|
||||||
|
return False
|
||||||
|
|
||||||
|
edges = study.river.enable_edges()
|
||||||
|
names = [
|
||||||
|
lc.name
|
||||||
|
for lc in study.river.lateral_contribution.get_tab("solid")
|
||||||
|
if not lc.is_deleted() and lc.reach in edges and len(lc) > 0
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(names) == 0:
|
||||||
|
self._status = STATUS.OK
|
||||||
|
self._summary = "ok"
|
||||||
|
return True
|
||||||
|
|
||||||
|
self._status = STATUS.WARNING
|
||||||
|
self._summary = _translate(
|
||||||
|
"Checker",
|
||||||
|
"Solid lateral contributions not taken into account by Mage: "
|
||||||
|
"{names}"
|
||||||
|
).format(names=", ".join(names))
|
||||||
|
return False
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
# RubarBE.py -- Pamhyr RubarBE checkers
|
||||||
|
# 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 -*-
|
||||||
|
|
||||||
|
"""RubarBE checkers. Each one runs a step of the checks of the RubarBE
|
||||||
|
export (Solver/RubarBE.py), without writing any file, so that they
|
||||||
|
always give the messages of the export: an error where the export would
|
||||||
|
be cancelled, a warning for the data it ignores (empty boundary
|
||||||
|
conditions, lateral contributions not read, solid lateral contributions
|
||||||
|
without liquid one, structures without RubarBE equivalent...)."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from Modules import Modules
|
||||||
|
from tools import logger_exception
|
||||||
|
|
||||||
|
from Checker.Checker import AbstractModelChecker, STATUS
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBEChecker(AbstractModelChecker):
|
||||||
|
"""Base class: _check(solver, study, context, warnings) runs the
|
||||||
|
export step, raising RubarExportError (error) or appending to
|
||||||
|
warnings (warning). Steps that need the reach are not run when the
|
||||||
|
study has not a single valid reach: RubarBEReachChecker reports it"""
|
||||||
|
|
||||||
|
_needs_reach = True
|
||||||
|
|
||||||
|
def __init__(self, name, description, modules, version="rubarbe"):
|
||||||
|
super(RubarBEChecker, self).__init__()
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._description = description
|
||||||
|
self._solver = version
|
||||||
|
self._modules = modules
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _context(solver, study):
|
||||||
|
# Read the parameters without creating them (get_params would
|
||||||
|
# add default ones to the study)
|
||||||
|
river = study.river
|
||||||
|
if solver.type in river._parameters:
|
||||||
|
values = solver._parameter_values(study)
|
||||||
|
else:
|
||||||
|
values = {k: str(v).strip()
|
||||||
|
for k, v in solver.default_parameters()}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"values": values,
|
||||||
|
"trased": values.get("rubarbe_trased", "n").lower()
|
||||||
|
in ("y", "o"),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def run(self, study):
|
||||||
|
from Solver.RubarBE import RubarBE, RubarExportError
|
||||||
|
|
||||||
|
if not self.basic_check(study):
|
||||||
|
return False
|
||||||
|
|
||||||
|
solver = RubarBE("checker")
|
||||||
|
warnings = []
|
||||||
|
try:
|
||||||
|
context = self._context(solver, study)
|
||||||
|
if self._needs_reach:
|
||||||
|
try:
|
||||||
|
edge, profiles = solver._single_reach(study.river)
|
||||||
|
except RubarExportError:
|
||||||
|
self._status = STATUS.OK
|
||||||
|
self._summary = "ok"
|
||||||
|
return True
|
||||||
|
context["hydraulics"] = {
|
||||||
|
"edge": edge,
|
||||||
|
"profiles": profiles,
|
||||||
|
"tinit": solver._seconds(
|
||||||
|
context["values"].get("rubarbe_tinit", "0")),
|
||||||
|
"warnings": warnings,
|
||||||
|
}
|
||||||
|
self._check(solver, study, context, warnings)
|
||||||
|
except (RubarExportError, ValueError) as error:
|
||||||
|
self._status = STATUS.ERROR
|
||||||
|
self._summary = str(error)
|
||||||
|
return False
|
||||||
|
except Exception as error:
|
||||||
|
# Never stop the checker thread
|
||||||
|
logger_exception(error)
|
||||||
|
self._status = STATUS.UNKNOWN
|
||||||
|
self._summary = str(error)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if warnings:
|
||||||
|
self._status = STATUS.WARNING
|
||||||
|
self._summary = " | ".join(warnings)
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._status = STATUS.OK
|
||||||
|
self._summary = "ok"
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBEReachChecker(RubarBEChecker):
|
||||||
|
_needs_reach = False
|
||||||
|
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBEReachChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE reach checker"),
|
||||||
|
_translate(
|
||||||
|
"Checker",
|
||||||
|
"Check that the study has a single enabled reach, with at "
|
||||||
|
"least two profiles of increasing rk"
|
||||||
|
),
|
||||||
|
Modules.GEOMETRY, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
solver._single_reach(study.river)
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBEGeometryChecker(RubarBEChecker):
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBEGeometryChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE geometry and sediment checker"),
|
||||||
|
_translate(
|
||||||
|
"Checker",
|
||||||
|
"Check the main channel and active bed limits of the "
|
||||||
|
"profiles and, with sediment transport, their sediment "
|
||||||
|
"layers"
|
||||||
|
),
|
||||||
|
# Rerun on changes of the geometry, which impact the sediment
|
||||||
|
# layers
|
||||||
|
Modules.SEDIMENT_LAYER, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
solver._geometry_data(context["hydraulics"]["edge"],
|
||||||
|
context["trased"])
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBEBoundaryChecker(RubarBEChecker):
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBEBoundaryChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE boundary conditions checker"),
|
||||||
|
_translate(
|
||||||
|
"Checker",
|
||||||
|
"Check the liquid and solid boundary conditions used by "
|
||||||
|
"the boundary settings of the solver parameters"
|
||||||
|
),
|
||||||
|
Modules.BOUNDARY_CONDITION, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
values = context["values"]
|
||||||
|
edge = context["hydraulics"]["edge"]
|
||||||
|
solver._check_regime(values)
|
||||||
|
solver._check_boundaries(study.river, edge, values, warnings)
|
||||||
|
solver._solid_boundary(study.river, edge, values,
|
||||||
|
context["trased"], warnings)
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBEFrictionChecker(RubarBEChecker):
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBEFrictionChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE friction checker"),
|
||||||
|
_translate("Checker", "Check that the reach has frictions"),
|
||||||
|
Modules.FRICTION, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
solver._interpolated_frictions(context["hydraulics"]["edge"])
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBEInitialConditionsChecker(RubarBEChecker):
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBEInitialConditionsChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE initial conditions checker"),
|
||||||
|
_translate(
|
||||||
|
"Checker", "Check the initial conditions of the reach"),
|
||||||
|
Modules.INITIAL_CONDITION, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
solver._initial_conditions(study.river,
|
||||||
|
context["hydraulics"]["edge"])
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBELateralChecker(RubarBEChecker):
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBELateralChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE lateral contributions checker"),
|
||||||
|
_translate(
|
||||||
|
"Checker",
|
||||||
|
"Check the lateral contributions: read only with lateral "
|
||||||
|
"overflows, solid ones attached to the liquid contribution "
|
||||||
|
"of the same sections"
|
||||||
|
),
|
||||||
|
Modules.LATERAL_CONTRIBUTION, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
solver._lateral_data(study.river, context["hydraulics"],
|
||||||
|
context["values"], context["trased"], warnings)
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBEStructureChecker(RubarBEChecker):
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBEStructureChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE hydraulic structures checker"),
|
||||||
|
_translate(
|
||||||
|
"Checker",
|
||||||
|
"Check the hydraulic structures: weirs and rectangular and "
|
||||||
|
"circular orifices only"
|
||||||
|
),
|
||||||
|
Modules.HYDRAULIC_STRUCTURES, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
solver._structure_data(study.river, context["hydraulics"], warnings)
|
||||||
|
|
||||||
|
|
||||||
|
class RubarBESavesChecker(RubarBEChecker):
|
||||||
|
def __init__(self, version="rubarbe"):
|
||||||
|
super(RubarBESavesChecker, self).__init__(
|
||||||
|
_translate("Checker", "RubarBE saves checker"),
|
||||||
|
_translate(
|
||||||
|
"Checker",
|
||||||
|
"Check the save abscissas (ABSHYD) and times (TNPROF) of "
|
||||||
|
"the solver parameters"
|
||||||
|
),
|
||||||
|
# The solver parameters have no module: rerun on any change
|
||||||
|
Modules.NONE, version,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check(self, solver, study, context, warnings):
|
||||||
|
solver._save_data(context["values"], context["hydraulics"])
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import queue
|
import queue
|
||||||
|
import types
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -842,7 +843,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
return poly
|
return poly
|
||||||
|
|
||||||
def wet_points(self, z):
|
def wet_points(self, z):
|
||||||
left, right = self.get_water_limits(z)
|
limits = self.get_water_limits(z)
|
||||||
|
|
||||||
|
if isinstance(limits, types.GeneratorType):
|
||||||
|
limits = next(limits)
|
||||||
|
left, right = limits
|
||||||
|
|
||||||
points = list(filter(lambda p: p.z < z, self._points))
|
points = list(filter(lambda p: p.z < z, self._points))
|
||||||
points = [left] + points + [right]
|
points = [left] + points + [right]
|
||||||
|
|||||||
@@ -224,6 +224,9 @@ class LateralContribution(SQLSubModel):
|
|||||||
self._header = []
|
self._header = []
|
||||||
self._types = [float, float]
|
self._types = [float, float]
|
||||||
|
|
||||||
|
self._d50 = 0.002
|
||||||
|
self._sigma = 1.0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute(f"""
|
execute(f"""
|
||||||
@@ -236,6 +239,8 @@ class LateralContribution(SQLSubModel):
|
|||||||
reach INTEGER,
|
reach INTEGER,
|
||||||
begin_section INTEGER,
|
begin_section INTEGER,
|
||||||
end_section INTEGER,
|
end_section INTEGER,
|
||||||
|
d50 REAL DEFAULT 0.002,
|
||||||
|
sigma REAL DEFAULT 1,
|
||||||
{Scenario.create_db_add_scenario()},
|
{Scenario.create_db_add_scenario()},
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
||||||
@@ -286,8 +291,32 @@ class LateralContribution(SQLSubModel):
|
|||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if major == "0" and (int(minor) < 2 or
|
||||||
|
(minor == "2" and int(release) < 11)):
|
||||||
|
cls._db_update_to_0_2_11(execute)
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update_to_0_2_11(cls, execute):
|
||||||
|
# The table may already have been recreated with these columns
|
||||||
|
# by _db_update_to_0_2_0
|
||||||
|
columns = [
|
||||||
|
row[0] for row in execute(
|
||||||
|
"SELECT name FROM pragma_table_info('lateral_contribution')"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if "d50" not in columns:
|
||||||
|
execute(
|
||||||
|
"ALTER TABLE lateral_contribution " +
|
||||||
|
"ADD COLUMN d50 REAL DEFAULT 0.002"
|
||||||
|
)
|
||||||
|
if "sigma" not in columns:
|
||||||
|
execute(
|
||||||
|
"ALTER TABLE lateral_contribution " +
|
||||||
|
"ADD COLUMN sigma REAL DEFAULT 1"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
def _db_update_to_0_2_0(cls, execute, data):
|
||||||
table = "lateral_contribution"
|
table = "lateral_contribution"
|
||||||
@@ -339,7 +368,7 @@ class LateralContribution(SQLSubModel):
|
|||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, type, " +
|
"SELECT pamhyr_id, deleted, name, type, " +
|
||||||
"reach, begin_section, end_section, scenario " +
|
"reach, begin_section, end_section, d50, sigma, scenario " +
|
||||||
"FROM lateral_contribution " +
|
"FROM lateral_contribution " +
|
||||||
f"WHERE tab = '{tab}'" +
|
f"WHERE tab = '{tab}'" +
|
||||||
f"AND scenario = {scenario.id} " +
|
f"AND scenario = {scenario.id} " +
|
||||||
@@ -356,6 +385,8 @@ class LateralContribution(SQLSubModel):
|
|||||||
reach = next(it)
|
reach = next(it)
|
||||||
b_section = next(it)
|
b_section = next(it)
|
||||||
e_section = next(it)
|
e_section = next(it)
|
||||||
|
d50 = next(it)
|
||||||
|
sigma = next(it)
|
||||||
owner_scenario = next(it)
|
owner_scenario = next(it)
|
||||||
|
|
||||||
ctor = cls._get_ctor_from_type(t)
|
ctor = cls._get_ctor_from_type(t)
|
||||||
@@ -367,6 +398,10 @@ class LateralContribution(SQLSubModel):
|
|||||||
lc.reach = None
|
lc.reach = None
|
||||||
lc._begin_section = None
|
lc._begin_section = None
|
||||||
lc._end_section = None
|
lc._end_section = None
|
||||||
|
if d50 is not None:
|
||||||
|
lc._d50 = float(d50)
|
||||||
|
if sigma is not None:
|
||||||
|
lc._sigma = float(sigma)
|
||||||
|
|
||||||
if row[3] != -1:
|
if row[3] != -1:
|
||||||
lc.reach = next(
|
lc.reach = next(
|
||||||
@@ -426,12 +461,13 @@ class LateralContribution(SQLSubModel):
|
|||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"lateral_contribution(" +
|
"lateral_contribution(" +
|
||||||
"pamhyr_id, deleted, name, type, tab, " +
|
"pamhyr_id, deleted, name, type, tab, " +
|
||||||
"reach, begin_section, end_section, scenario) " +
|
"reach, begin_section, end_section, d50, sigma, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._type)}', '{tab}', {reach}, " +
|
f"'{self._db_format(self._type)}', '{tab}', {reach}, " +
|
||||||
f"{begin_section}, {end_section}, " +
|
f"{begin_section}, {end_section}, " +
|
||||||
|
f"{self._d50}, {self._sigma}, " +
|
||||||
f"{self._status.scenario_id}" +
|
f"{self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
@@ -523,10 +559,30 @@ class LateralContribution(SQLSubModel):
|
|||||||
new._reach = reach
|
new._reach = reach
|
||||||
new._begin_section = begin_section
|
new._begin_section = begin_section
|
||||||
new._end_section = end_section
|
new._end_section = end_section
|
||||||
|
new._d50 = self._d50
|
||||||
|
new._sigma = self._sigma
|
||||||
new._data = [data.cloned() for data in self.data]
|
new._data = [data.cloned() for data in self.data]
|
||||||
new.modified()
|
new.modified()
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
@property
|
||||||
|
def d50(self):
|
||||||
|
return self._d50
|
||||||
|
|
||||||
|
@d50.setter
|
||||||
|
def d50(self, value):
|
||||||
|
self._d50 = float(value)
|
||||||
|
self.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sigma(self):
|
||||||
|
return self._sigma
|
||||||
|
|
||||||
|
@sigma.setter
|
||||||
|
def sigma(self, value):
|
||||||
|
self._sigma = float(value)
|
||||||
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def begin_rk(self):
|
def begin_rk(self):
|
||||||
if self._begin_section is None:
|
if self._begin_section is None:
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
from Solver.Solvers import solver_type_list
|
from Solver.Solvers import solver_type_list
|
||||||
|
from Solver.RubarBE import Rubar3
|
||||||
|
|
||||||
from Model.Scenario import Scenario
|
from Model.Scenario import Scenario
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
@@ -296,14 +297,20 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
scenario = data["scenario"]
|
scenario = data["scenario"]
|
||||||
loaded = data['loaded_pid']
|
loaded = data['loaded_pid']
|
||||||
|
|
||||||
solvers = execute("SELECT DISTINCT solver FROM solver_parameter")
|
solvers = [
|
||||||
|
row[0] for row in
|
||||||
|
execute("SELECT DISTINCT solver FROM solver_parameter")
|
||||||
|
]
|
||||||
new = {}
|
new = {}
|
||||||
|
|
||||||
if scenario is None:
|
if scenario is None:
|
||||||
return new
|
return new
|
||||||
|
|
||||||
for solver in solvers:
|
for solver in solvers:
|
||||||
solver = solver[0]
|
if solver == "rubar3" and "rubarbe" not in solvers:
|
||||||
|
new["rubarbe"] = cls._db_load_rubar3(execute, data)
|
||||||
|
continue
|
||||||
|
|
||||||
if solver not in solver_type_list:
|
if solver not in solver_type_list:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -327,6 +334,32 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load_rubar3(cls, execute, data):
|
||||||
|
"""Rubar3 is no longer offered: RubarBE, its generic version,
|
||||||
|
takes the parameters of a Rubar3 study (same rubarbe_* keys,
|
||||||
|
RubarBE reads them the same way). Missing parameters take the
|
||||||
|
Rubar3 defaults (no sediment transport), so that the
|
||||||
|
computation is unchanged. New parameters (new ids): saved as
|
||||||
|
RubarBE ones with the study, the Rubar3 rows are then ignored"""
|
||||||
|
status = data["status"]
|
||||||
|
n = cls(solver_type=solver_type_list["rubarbe"], status=status)
|
||||||
|
|
||||||
|
data['solver'] = "rubar3"
|
||||||
|
lst = Parameter._db_load(execute, data)
|
||||||
|
|
||||||
|
n._lst = [
|
||||||
|
Parameter(name=p.name, value=p.value, status=status)
|
||||||
|
for _, p in sorted(lst, key=lambda t: t[0])
|
||||||
|
]
|
||||||
|
names = {p.name for p in n._lst}
|
||||||
|
n._lst += [
|
||||||
|
Parameter(name=name, value=value, status=status)
|
||||||
|
for name, value in Rubar3.default_parameters()
|
||||||
|
if name not in names
|
||||||
|
]
|
||||||
|
return n
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
t = self._solver._type
|
t = self._solver._type
|
||||||
execute(
|
execute(
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
class Study(SQLModel):
|
class Study(SQLModel):
|
||||||
_version = "0.2.10"
|
_version = "0.2.11"
|
||||||
|
|
||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
Scenario,
|
Scenario,
|
||||||
|
|||||||
+3
-1
@@ -28,6 +28,7 @@ from tools import timer, trace, logger_exception
|
|||||||
from Solver.CommandLine import CommandLineSolver
|
from Solver.CommandLine import CommandLineSolver
|
||||||
from Checker.Mage import (
|
from Checker.Mage import (
|
||||||
MageNetworkGraphChecker, MageGeometryGuideLineChecker,
|
MageNetworkGraphChecker, MageGeometryGuideLineChecker,
|
||||||
|
MageLateralSolidChecker,
|
||||||
)
|
)
|
||||||
|
|
||||||
from Model.Results.Results import Results
|
from Model.Results.Results import Results
|
||||||
@@ -106,6 +107,7 @@ class Mage(CommandLineSolver):
|
|||||||
MageNetworkGraphChecker(connectivity=True, version=cls._type),
|
MageNetworkGraphChecker(connectivity=True, version=cls._type),
|
||||||
MageNetworkGraphChecker(connectivity=False, version=cls._type),
|
MageNetworkGraphChecker(connectivity=False, version=cls._type),
|
||||||
MageGeometryGuideLineChecker(version=cls._type),
|
MageGeometryGuideLineChecker(version=cls._type),
|
||||||
|
MageLateralSolidChecker(version=cls._type),
|
||||||
]
|
]
|
||||||
|
|
||||||
return lst
|
return lst
|
||||||
@@ -391,7 +393,7 @@ class Mage(CommandLineSolver):
|
|||||||
qlog.put(f"Export LAT file")
|
qlog.put(f"Export LAT file")
|
||||||
files.append(f"{name}.LAT")
|
files.append(f"{name}.LAT")
|
||||||
|
|
||||||
for tab in ["liquid", "solid", "suspenssion"]:
|
for tab in ["liquid"]:
|
||||||
for lateral in lst.get_tab(tab):
|
for lateral in lst.get_tab(tab):
|
||||||
self._export_LC(study, lateral, f, qlog)
|
self._export_LC(study, lateral, f, qlog)
|
||||||
|
|
||||||
|
|||||||
+122
-40
@@ -123,10 +123,21 @@ class Rubar3(CommandLineSolver):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def checkers(cls):
|
def checkers(cls):
|
||||||
lst = [
|
from Checker.RubarBE import (
|
||||||
]
|
RubarBEReachChecker, RubarBEGeometryChecker,
|
||||||
|
RubarBEBoundaryChecker, RubarBEFrictionChecker,
|
||||||
|
RubarBEInitialConditionsChecker, RubarBELateralChecker,
|
||||||
|
RubarBEStructureChecker, RubarBESavesChecker,
|
||||||
|
)
|
||||||
|
|
||||||
return lst
|
return [
|
||||||
|
checker(version=cls._type) for checker in (
|
||||||
|
RubarBEReachChecker, RubarBEGeometryChecker,
|
||||||
|
RubarBEBoundaryChecker, RubarBEFrictionChecker,
|
||||||
|
RubarBEInitialConditionsChecker, RubarBELateralChecker,
|
||||||
|
RubarBEStructureChecker, RubarBESavesChecker,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
##########
|
##########
|
||||||
# Export #
|
# Export #
|
||||||
@@ -154,19 +165,11 @@ class Rubar3(CommandLineSolver):
|
|||||||
name = study.name.replace(" ", "_")
|
name = study.name.replace(" ", "_")
|
||||||
|
|
||||||
values = self._parameter_values(study)
|
values = self._parameter_values(study)
|
||||||
if (values.get("rubarbe_regime") == "1" and
|
|
||||||
values.get("rubarbe_condam") == "0"):
|
|
||||||
trad = RunSolverTranslate()
|
|
||||||
message = trad["incompatible_upstream_boundary"]
|
|
||||||
logger.warning(message)
|
|
||||||
if qlog is not None:
|
|
||||||
qlog.put(trad["warning"].format(message=message))
|
|
||||||
return False
|
|
||||||
|
|
||||||
trased = self._parameter_flag(study, "trased") == "O"
|
trased = self._parameter_flag(study, "trased") == "O"
|
||||||
|
|
||||||
# Check the hydraulic data before writing any file
|
# Check the hydraulic data before writing any file
|
||||||
try:
|
try:
|
||||||
|
self._check_regime(values)
|
||||||
hydraulics = self._hydraulic_data(study, values)
|
hydraulics = self._hydraulic_data(study, values)
|
||||||
geometry = self._geometry_data(hydraulics["edge"], trased)
|
geometry = self._geometry_data(hydraulics["edge"], trased)
|
||||||
solid = self._solid_boundary(
|
solid = self._solid_boundary(
|
||||||
@@ -174,7 +177,8 @@ class Rubar3(CommandLineSolver):
|
|||||||
hydraulics["warnings"])
|
hydraulics["warnings"])
|
||||||
saves = self._save_data(values, hydraulics)
|
saves = self._save_data(values, hydraulics)
|
||||||
laterals = self._lateral_data(
|
laterals = self._lateral_data(
|
||||||
study.river, hydraulics, values, hydraulics["warnings"])
|
study.river, hydraulics, values, trased,
|
||||||
|
hydraulics["warnings"])
|
||||||
structures = self._structure_data(
|
structures = self._structure_data(
|
||||||
study.river, hydraulics, hydraulics["warnings"])
|
study.river, hydraulics, hydraulics["warnings"])
|
||||||
except RubarExportError as error:
|
except RubarExportError as error:
|
||||||
@@ -231,13 +235,20 @@ class Rubar3(CommandLineSolver):
|
|||||||
# NCLMAX (variables.f90): points of all the solid hydrographs
|
# NCLMAX (variables.f90): points of all the solid hydrographs
|
||||||
_max_solid_points = 1000
|
_max_solid_points = 1000
|
||||||
|
|
||||||
def _hydraulic_data(self, study, values):
|
@staticmethod
|
||||||
"""Check the single reach data needed by DONNEE, MAIL, CONDIN,
|
def _check_regime(values):
|
||||||
FROT, HYDRO and CONDAV, and return them. Raise
|
"""REGIME=1 imposes the water depth upstream, which a reflecting
|
||||||
RubarExportError with a user message"""
|
upstream boundary (CONDAM=0) cannot do"""
|
||||||
trad = RunSolverTranslate()
|
if (values.get("rubarbe_regime") == "1" and
|
||||||
river = study.river
|
values.get("rubarbe_condam") == "0"):
|
||||||
|
raise RubarExportError(
|
||||||
|
RunSolverTranslate()["incompatible_upstream_boundary"])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _single_reach(river):
|
||||||
|
"""The single enabled reach and its profiles, with at least two
|
||||||
|
profiles of increasing rk. Raise RubarExportError"""
|
||||||
|
trad = RunSolverTranslate()
|
||||||
edges = river.enable_edges()
|
edges = river.enable_edges()
|
||||||
if len(edges) != 1:
|
if len(edges) != 1:
|
||||||
raise RubarExportError(trad["multireach_unsupported"].format(
|
raise RubarExportError(trad["multireach_unsupported"].format(
|
||||||
@@ -251,6 +262,14 @@ class Rubar3(CommandLineSolver):
|
|||||||
if any(b <= a for a, b in zip(rks, rks[1:])):
|
if any(b <= a for a, b in zip(rks, rks[1:])):
|
||||||
raise RubarExportError(trad["rk_not_increasing"].format(
|
raise RubarExportError(trad["rk_not_increasing"].format(
|
||||||
reach=edge.name))
|
reach=edge.name))
|
||||||
|
return edge, profiles
|
||||||
|
|
||||||
|
def _hydraulic_data(self, study, values):
|
||||||
|
"""Check the single reach data needed by DONNEE, MAIL, CONDIN,
|
||||||
|
FROT, HYDRO and CONDAV, and return them. Raise
|
||||||
|
RubarExportError with a user message"""
|
||||||
|
river = study.river
|
||||||
|
edge, profiles = self._single_reach(river)
|
||||||
|
|
||||||
warnings = []
|
warnings = []
|
||||||
boundaries = self._check_boundaries(river, edge, values, warnings)
|
boundaries = self._check_boundaries(river, edge, values, warnings)
|
||||||
@@ -546,7 +565,7 @@ class Rubar3(CommandLineSolver):
|
|||||||
# inflow in each cell of its zone, without checking this size
|
# inflow in each cell of its zone, without checking this size
|
||||||
_max_lateral_points = 3000 * 20
|
_max_lateral_points = 3000 * 20
|
||||||
|
|
||||||
def _lateral_data(self, river, hydraulics, values, warnings):
|
def _lateral_data(self, river, hydraulics, values, trased, warnings):
|
||||||
"""Lateral inflows of HYDEVS, read when IODEV=O (rubar_init.f90,
|
"""Lateral inflows of HYDEVS, read when IODEV=O (rubar_init.f90,
|
||||||
init2). Return [(first cell, last cell, [(t, q), ...]), ...]
|
init2). Return [(first cell, last cell, [(t, q), ...]), ...]
|
||||||
sorted from upstream, or None when IODEV=N.
|
sorted from upstream, or None when IODEV=N.
|
||||||
@@ -557,9 +576,17 @@ class Rubar3(CommandLineSolver):
|
|||||||
b covers the cell downstream of it (upstream of it for the last
|
b covers the cell downstream of it (upstream of it for the last
|
||||||
section). The first and last cells of the reach are never used,
|
section). The first and last cells of the reach are never used,
|
||||||
as RubarBE requires. With IODEV=N, the contributions are ignored
|
as RubarBE requires. With IODEV=N, the contributions are ignored
|
||||||
with a warning. Otherwise, solid and suspension contributions have
|
with a warning.
|
||||||
no RubarBE equivalent (their sediment cannot be linked to an
|
|
||||||
inflow): they are refused"""
|
RubarBE brings sediment only with a liquid inflow, on the same
|
||||||
|
cells: a solid contribution (kg/s, UNISOL=1) is attached to the
|
||||||
|
liquid contribution of the same zone, whose block gets its D50
|
||||||
|
and sigma; the times of both series are merged (linear
|
||||||
|
interpolation, constant outside). Several solid contributions of
|
||||||
|
a zone are summed when they have the same grain size. A solid
|
||||||
|
contribution without liquid one, or without sediment transport,
|
||||||
|
is ignored with a warning. A liquid inflow without solid
|
||||||
|
contribution brings clear water (Qs = 0, D = S = 0)."""
|
||||||
trad = RunSolverTranslate()
|
trad = RunSolverTranslate()
|
||||||
edge = hydraulics["edge"]
|
edge = hydraulics["edge"]
|
||||||
profiles = hydraulics["profiles"]
|
profiles = hydraulics["profiles"]
|
||||||
@@ -580,40 +607,50 @@ class Rubar3(CommandLineSolver):
|
|||||||
return kept
|
return kept
|
||||||
|
|
||||||
lcs = on_reach("liquid")
|
lcs = on_reach("liquid")
|
||||||
sediment = on_reach("solid") + on_reach("suspenssion")
|
solids = on_reach("solid")
|
||||||
|
|
||||||
# Not read by RubarBE: the user is warned, the computation runs
|
# Not read by RubarBE: the user is warned, the computation runs
|
||||||
# without them
|
# without them
|
||||||
if not iodev:
|
if not iodev:
|
||||||
for lc in lcs + sediment:
|
for lc in lcs + solids:
|
||||||
warnings.append(trad["lateral_iodev_disabled"].format(
|
warnings.append(trad["lateral_iodev_disabled"].format(
|
||||||
name=lc.name, reach=edge.name))
|
name=lc.name, reach=edge.name))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if sediment:
|
if not trased:
|
||||||
raise RubarExportError(trad["lateral_solid_unsupported"].format(
|
for lc in solids:
|
||||||
name=sediment[0].name, reach=edge.name))
|
warnings.append(trad["lateral_solid_no_transport"].format(
|
||||||
|
name=lc.name, reach=edge.name))
|
||||||
|
solids = []
|
||||||
|
if solids and values.get("rubarbe_sediment_unisol", "1") != "1":
|
||||||
|
raise RubarExportError(trad["lateral_solid_unisol"].format(
|
||||||
|
name=solids[0].name, reach=edge.name))
|
||||||
|
|
||||||
index = {id(p): i for i, p in enumerate(profiles, 1)}
|
index = {id(p): i for i, p in enumerate(profiles, 1)}
|
||||||
count = len(profiles)
|
count = len(profiles)
|
||||||
blocks = []
|
|
||||||
for lc in lcs:
|
def zone_and_data(lc):
|
||||||
if lc.lctype not in self._lateral_signs:
|
|
||||||
raise RubarExportError(trad["lateral_type_unsupported"].format(
|
|
||||||
name=lc.name, reach=edge.name))
|
|
||||||
if (id(lc.begin_section) not in index or
|
if (id(lc.begin_section) not in index or
|
||||||
id(lc.end_section) not in index):
|
id(lc.end_section) not in index):
|
||||||
raise RubarExportError(trad["lateral_sections"].format(
|
raise RubarExportError(trad["lateral_sections"].format(
|
||||||
name=lc.name, reach=edge.name))
|
name=lc.name, reach=edge.name))
|
||||||
b, e = sorted((index[id(lc.begin_section)],
|
b, e = sorted((index[id(lc.begin_section)],
|
||||||
index[id(lc.end_section)]))
|
index[id(lc.end_section)]))
|
||||||
first, last = (b + 1, e) if b < e else ((min(b + 1, count),) * 2)
|
zone = (b + 1, e) if b < e else ((min(b + 1, count),) * 2)
|
||||||
|
|
||||||
data = [(float(d[0]), float(d[1])) for d in lc.data]
|
data = [(float(d[0]), float(d[1])) for d in lc.data]
|
||||||
times = [t for t, _ in data]
|
times = [t for t, _ in data]
|
||||||
if any(t1 < t0 for t0, t1 in zip(times, times[1:])):
|
if any(t1 < t0 for t0, t1 in zip(times, times[1:])):
|
||||||
raise RubarExportError(trad["lateral_not_sorted"].format(
|
raise RubarExportError(trad["lateral_not_sorted"].format(
|
||||||
name=lc.name, reach=edge.name))
|
name=lc.name, reach=edge.name))
|
||||||
|
return zone, data
|
||||||
|
|
||||||
|
blocks = []
|
||||||
|
for lc in lcs:
|
||||||
|
if lc.lctype not in self._lateral_signs:
|
||||||
|
raise RubarExportError(trad["lateral_type_unsupported"].format(
|
||||||
|
name=lc.name, reach=edge.name))
|
||||||
|
(first, last), data = zone_and_data(lc)
|
||||||
sign = self._lateral_signs[lc.lctype]
|
sign = self._lateral_signs[lc.lctype]
|
||||||
blocks.append((first, last, [(t, sign * q) for t, q in data],
|
blocks.append((first, last, [(t, sign * q) for t, q in data],
|
||||||
lc.name))
|
lc.name))
|
||||||
@@ -625,13 +662,56 @@ class Rubar3(CommandLineSolver):
|
|||||||
raise RubarExportError(trad["lateral_overlap"].format(
|
raise RubarExportError(trad["lateral_overlap"].format(
|
||||||
first=name0, second=name1, reach=edge.name))
|
first=name0, second=name1, reach=edge.name))
|
||||||
|
|
||||||
|
# Solid contributions grouped by the zone of their liquid one
|
||||||
|
zones = {(first, last) for first, last, _, _ in blocks}
|
||||||
|
sediment = {}
|
||||||
|
for lc in solids:
|
||||||
|
zone, data = zone_and_data(lc)
|
||||||
|
if not (lc.d50 > 0 and lc.sigma >= 1):
|
||||||
|
raise RubarExportError(trad["lateral_solid_invalid"].format(
|
||||||
|
name=lc.name, reach=edge.name))
|
||||||
|
if zone not in zones:
|
||||||
|
warnings.append(trad["lateral_solid_unmatched"].format(
|
||||||
|
name=lc.name, reach=edge.name))
|
||||||
|
continue
|
||||||
|
sediment.setdefault(zone, []).append((lc, data))
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for first, last, data, name in blocks:
|
||||||
|
attached = sediment.get((first, last), [])
|
||||||
|
grains = {(lc.d50, lc.sigma) for lc, _ in attached}
|
||||||
|
if len(grains) > 1:
|
||||||
|
raise RubarExportError(trad["lateral_solid_conflict"].format(
|
||||||
|
names=", ".join(lc.name for lc, _ in attached),
|
||||||
|
liquid=name, reach=edge.name))
|
||||||
|
|
||||||
|
times = sorted({t for t, _ in data} |
|
||||||
|
{t for _, solid in attached for t, _ in solid})
|
||||||
|
|
||||||
|
def at(series):
|
||||||
|
return np.interp(times, [t for t, _ in series],
|
||||||
|
[v for _, v in series])
|
||||||
|
|
||||||
|
q = at(data)
|
||||||
|
qs = sum((at(solid) for _, solid in attached),
|
||||||
|
np.zeros(len(times)))
|
||||||
|
length = (self._section_rk(profiles[last - 1]) -
|
||||||
|
self._section_rk(profiles[first - 2]))
|
||||||
|
result.append((
|
||||||
|
first, last,
|
||||||
|
[(float(t), float(v), float(vs))
|
||||||
|
for t, v, vs in zip(times, q, qs)],
|
||||||
|
next(iter(grains), (0.0, 0.0)),
|
||||||
|
length,
|
||||||
|
))
|
||||||
|
|
||||||
points = sum((last - first + 1) * len(data)
|
points = sum((last - first + 1) * len(data)
|
||||||
for first, last, data, _ in blocks)
|
for first, last, data, _, _ in result)
|
||||||
if points > self._max_lateral_points:
|
if points > self._max_lateral_points:
|
||||||
raise RubarExportError(trad["lateral_too_long"].format(
|
raise RubarExportError(trad["lateral_too_long"].format(
|
||||||
count=points, maximum=self._max_lateral_points))
|
count=points, maximum=self._max_lateral_points))
|
||||||
|
|
||||||
return [block[:3] for block in blocks]
|
return result
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# Structures #
|
# Structures #
|
||||||
@@ -1251,13 +1331,15 @@ class Rubar3(CommandLineSolver):
|
|||||||
if qlog is not None:
|
if qlog is not None:
|
||||||
qlog.put("Export HYDEVS file")
|
qlog.put("Export HYDEVS file")
|
||||||
lines = []
|
lines = []
|
||||||
for first, last, data in laterals:
|
for first, last, data, (d50, sigma), length in laterals:
|
||||||
if trased:
|
if trased:
|
||||||
lines.append(f"{first} {last} {len(data)} 0.0 0.0\n")
|
lines.append(
|
||||||
lines += [f"{q!r} {t!r} 0.0 {t!r}\n" for t, q in data]
|
f"{first} {last} {len(data)} {d50!r} {sigma!r}\n")
|
||||||
|
lines += [f"{q!r} {t!r} {qs / length!r} {t!r}\n"
|
||||||
|
for t, q, qs in data]
|
||||||
else:
|
else:
|
||||||
lines.append(f"{first} {last} {len(data)}\n")
|
lines.append(f"{first} {last} {len(data)}\n")
|
||||||
lines += [f"{q!r} {t!r}\n" for t, q in data]
|
lines += [f"{q!r} {t!r}\n" for t, q, _ in data]
|
||||||
self._write_parameters(repertory, f"hydevs.{name}", lines)
|
self._write_parameters(repertory, f"hydevs.{name}", lines)
|
||||||
|
|
||||||
def _export_ouvrag(self, structures, repertory, qlog, name="0"):
|
def _export_ouvrag(self, structures, repertory, qlog, name="0"):
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ solver_long_name = {
|
|||||||
"adistswc": "Adis-TS_WC",
|
"adistswc": "Adis-TS_WC",
|
||||||
"adisttwc": "Adis-TT_WC",
|
"adisttwc": "Adis-TT_WC",
|
||||||
"rubarbe": "RubarBE",
|
"rubarbe": "RubarBE",
|
||||||
"rubar3": "Rubar3",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
solver_type_list = {
|
solver_type_list = {
|
||||||
@@ -47,5 +46,4 @@ solver_type_list = {
|
|||||||
"adistswc": AdisTSwc,
|
"adistswc": AdisTSwc,
|
||||||
"adisttwc": AdisTTwc,
|
"adisttwc": AdisTTwc,
|
||||||
"rubarbe": RubarBE,
|
"rubarbe": RubarBE,
|
||||||
"rubar3": Rubar3,
|
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,10 @@ class EditBoundaryConditionWindow(PamhyrWindow):
|
|||||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
self.canvas.setObjectName("canvas")
|
self.canvas.setObjectName("canvas")
|
||||||
self.toolbar = PamhyrPlotToolbar(
|
self.toolbar = PamhyrPlotToolbar(
|
||||||
self.canvas, self
|
self.canvas, self, items=[
|
||||||
|
"home", "move", "zoom", "save",
|
||||||
|
"iso", "back/forward"
|
||||||
|
]
|
||||||
)
|
)
|
||||||
self.verticalLayout.addWidget(self.toolbar)
|
self.verticalLayout.addWidget(self.toolbar)
|
||||||
self.verticalLayout.addWidget(self.canvas)
|
self.verticalLayout.addWidget(self.canvas)
|
||||||
|
|||||||
@@ -105,9 +105,19 @@ class ComboBoxDelegate(QItemDelegate):
|
|||||||
strickler
|
strickler
|
||||||
)
|
)
|
||||||
elif self._mode == "rk":
|
elif self._mode == "rk":
|
||||||
self.editor.addItems(
|
if self._data is None:
|
||||||
list(map(str, self._data.reach.get_rk()))
|
self.editor.addItems(
|
||||||
)
|
["-"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.editor.addItems(
|
||||||
|
list(
|
||||||
|
map(
|
||||||
|
lambda p: p.display_name(),
|
||||||
|
self._data.reach.profiles
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
|
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
|
||||||
return self.editor
|
return self.editor
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class PlotAC(PamhyrPlot):
|
|||||||
self._isometric_axis = False
|
self._isometric_axis = False
|
||||||
|
|
||||||
self._auto_relim_update = True
|
self._auto_relim_update = True
|
||||||
self._autoscale_update = False
|
self._autoscale_update = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def river(self):
|
def river(self):
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ _translate = QCoreApplication.translate
|
|||||||
|
|
||||||
|
|
||||||
class PlotRKC(PamhyrPlot):
|
class PlotRKC(PamhyrPlot):
|
||||||
|
# Class attributes: PlotRKZ (frictions) does not call this __init__
|
||||||
|
_selected = None # Selected hydraulic structure, in blue
|
||||||
|
profile = None
|
||||||
|
line_rk_zmin = None
|
||||||
|
line_rk_zmin_zmax = None
|
||||||
|
hs_vlines = None
|
||||||
|
_hs_drawn = [] # Structures of hs_vlines, in order
|
||||||
|
|
||||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
river=None, reach=None, profile=None,
|
river=None, reach=None, profile=None,
|
||||||
parent=None):
|
parent=None):
|
||||||
@@ -134,19 +142,14 @@ class PlotRKC(PamhyrPlot):
|
|||||||
vymin = []
|
vymin = []
|
||||||
vymax = []
|
vymax = []
|
||||||
self.anotate_lst = []
|
self.anotate_lst = []
|
||||||
|
self._hs_drawn = []
|
||||||
hs_color = []
|
hs_color = []
|
||||||
index = self.parent.tableView.selectedIndexes()
|
for hs in lhs:
|
||||||
for i, hs in enumerate(lhs):
|
|
||||||
section = hs.input_section
|
section = hs.input_section
|
||||||
if section is None:
|
if section is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if i == index[0].row():
|
color = self._hs_color(hs)
|
||||||
color = "blue"
|
|
||||||
elif hs.enabled:
|
|
||||||
color = "red"
|
|
||||||
else:
|
|
||||||
color = "darkgrey"
|
|
||||||
|
|
||||||
x = section.rk
|
x = section.rk
|
||||||
if x is not None:
|
if x is not None:
|
||||||
@@ -164,6 +167,7 @@ class PlotRKC(PamhyrPlot):
|
|||||||
vymin.append(min(z_min))
|
vymin.append(min(z_min))
|
||||||
vymax.append(max(z_max))
|
vymax.append(max(z_max))
|
||||||
hs_color.append(color)
|
hs_color.append(color)
|
||||||
|
self._hs_drawn.append(hs)
|
||||||
|
|
||||||
self.hs_vlines = self.canvas.axes.vlines(
|
self.hs_vlines = self.canvas.axes.vlines(
|
||||||
x=vx, ymin=vymin, ymax=vymax,
|
x=vx, ymin=vymin, ymax=vymax,
|
||||||
@@ -172,6 +176,28 @@ class PlotRKC(PamhyrPlot):
|
|||||||
color=hs_color,
|
color=hs_color,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _hs_color(self, hs):
|
||||||
|
if hs is self._selected:
|
||||||
|
return "blue"
|
||||||
|
if hs.enabled:
|
||||||
|
return "red"
|
||||||
|
return "darkgrey"
|
||||||
|
|
||||||
|
def set_selected(self, hs):
|
||||||
|
"""Draw the selected structure in blue, without redrawing the
|
||||||
|
plot (the view is kept)"""
|
||||||
|
self._selected = hs
|
||||||
|
if self.hs_vlines is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
colors = [self._hs_color(h) for h in self._hs_drawn]
|
||||||
|
if colors:
|
||||||
|
self.hs_vlines.set_color(colors)
|
||||||
|
for annotation, color in zip(self.anotate_lst, colors):
|
||||||
|
annotation.set_color(color)
|
||||||
|
|
||||||
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def color_highlight(self, highlight):
|
def color_highlight(self, highlight):
|
||||||
|
|
||||||
reach = self._current_reach
|
reach = self._current_reach
|
||||||
@@ -255,7 +281,8 @@ class PlotRKC(PamhyrPlot):
|
|||||||
|
|
||||||
closest = self._closest_profile(event)
|
closest = self._closest_profile(event)
|
||||||
index = self.parent.tableView.selectedIndexes()
|
index = self.parent.tableView.selectedIndexes()
|
||||||
if self.parent._table is not None:
|
# Moves the selected structure: nothing to do without selection
|
||||||
|
if self.parent._table is not None and len(index) > 2:
|
||||||
self.parent._table.setData(index[2], closest)
|
self.parent._table.setData(index[2], closest)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -130,11 +130,13 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
|||||||
self._checkbox = self.find(QCheckBox, f"checkBox")
|
self._checkbox = self.find(QCheckBox, f"checkBox")
|
||||||
self._set_checkbox_state()
|
self._set_checkbox_state()
|
||||||
|
|
||||||
|
_toolbar_items = ["home", "move", "zoom", "save", "iso", "back/forward"]
|
||||||
|
|
||||||
def setup_plots(self):
|
def setup_plots(self):
|
||||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
self.canvas.setObjectName("canvas")
|
self.canvas.setObjectName("canvas")
|
||||||
self.toolbar = PamhyrPlotToolbar(
|
self.toolbar = PamhyrPlotToolbar(
|
||||||
self.canvas, self
|
self.canvas, self, items=self._toolbar_items
|
||||||
)
|
)
|
||||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||||
self.plot_layout.addWidget(self.toolbar)
|
self.plot_layout.addWidget(self.toolbar)
|
||||||
@@ -143,7 +145,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
|||||||
self.plot_rkc = PlotRKC(
|
self.plot_rkc = PlotRKC(
|
||||||
canvas=self.canvas,
|
canvas=self.canvas,
|
||||||
river=self._study.river,
|
river=self._study.river,
|
||||||
reach=None,
|
reach=self._displayed_reach(None),
|
||||||
profile=None,
|
profile=None,
|
||||||
trad=self._trad,
|
trad=self._trad,
|
||||||
toolbar=self.toolbar,
|
toolbar=self.toolbar,
|
||||||
@@ -154,7 +156,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
|||||||
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
|
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
|
||||||
self.canvas_2.setObjectName("canvas_2")
|
self.canvas_2.setObjectName("canvas_2")
|
||||||
self.toolbar_2 = PamhyrPlotToolbar(
|
self.toolbar_2 = PamhyrPlotToolbar(
|
||||||
self.canvas_2, self
|
self.canvas_2, self, items=self._toolbar_items
|
||||||
)
|
)
|
||||||
self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2")
|
self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2")
|
||||||
self.plot_layout_2.addWidget(self.toolbar_2)
|
self.plot_layout_2.addWidget(self.toolbar_2)
|
||||||
@@ -183,7 +185,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
|||||||
table = self.find(QTableView, "tableView")
|
table = self.find(QTableView, "tableView")
|
||||||
table.selectionModel()\
|
table.selectionModel()\
|
||||||
.selectionChanged\
|
.selectionChanged\
|
||||||
.connect(self.update)
|
.connect(self.update_selection)
|
||||||
|
|
||||||
self._table.dataChanged.connect(self.update)
|
self._table.dataChanged.connect(self.update)
|
||||||
self._table.layoutChanged.connect(self.update)
|
self._table.layoutChanged.connect(self.update)
|
||||||
@@ -283,10 +285,17 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
"""Data changed: plots redrawn, fitted to their data"""
|
||||||
self._set_checkbox_state()
|
self._set_checkbox_state()
|
||||||
self._update_clear_plot()
|
self._update_plots(redraw=True)
|
||||||
|
|
||||||
def _update_clear_plot(self):
|
def update_selection(self):
|
||||||
|
"""Selection changed: the selected structure is drawn in blue,
|
||||||
|
the view is kept (unless its reach is another one)"""
|
||||||
|
self._set_checkbox_state()
|
||||||
|
self._update_plots(redraw=False)
|
||||||
|
|
||||||
|
def _selected_structure(self):
|
||||||
rows = self.index_selected_rows()
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
if (len(rows) == 0 or
|
if (len(rows) == 0 or
|
||||||
|
|||||||
@@ -384,9 +384,19 @@ class InitialConditionTableModel(PamhyrTableModel):
|
|||||||
self.layoutChanged.emit()
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
def generate(self, generator, param, option):
|
def generate(self, generator, param, option):
|
||||||
|
self.layoutAboutToBeChanged.emit()
|
||||||
|
|
||||||
|
self._undo.beginMacro(f"generate {generator}")
|
||||||
self._undo.push(
|
self._undo.push(
|
||||||
GenerateCommand(
|
GenerateCommand(
|
||||||
self._lst, generator, param, option
|
self._lst, generator, param, option
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self._undo.push(
|
||||||
|
SortCommand(
|
||||||
|
self._lst, False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._undo.endMacro()
|
||||||
|
|
||||||
self.layoutChanged.emit()
|
self.layoutChanged.emit()
|
||||||
@@ -100,15 +100,19 @@ class SortCommand(QUndoCommand):
|
|||||||
self._indexes = None
|
self._indexes = None
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
ll = self._ics.data
|
order = {
|
||||||
|
id(x): self._indexes[i]
|
||||||
|
for i, x in enumerate(self._ics.data)
|
||||||
|
}
|
||||||
self._ics.sort(
|
self._ics.sort(
|
||||||
key=lambda x: self._indexes[ll.index(x)]
|
key=lambda x: order.get(id(x), len(order))
|
||||||
)
|
)
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
self._ics.sort(
|
self._ics.sort(
|
||||||
reverse=self._reverse,
|
reverse=self._reverse,
|
||||||
key=lambda x: x["rk"].rk
|
key=lambda x: ((x["rk"] is None),
|
||||||
|
0.0 if x["rk"] is None else x["rk"].rk)
|
||||||
)
|
)
|
||||||
if self._indexes is None:
|
if self._indexes is None:
|
||||||
self._indexes = list(
|
self._indexes = list(
|
||||||
@@ -182,11 +186,19 @@ class GenerateCommand(QUndoCommand):
|
|||||||
self._ics = ics
|
self._ics = ics
|
||||||
self._param = param
|
self._param = param
|
||||||
self._option = option
|
self._option = option
|
||||||
self._copy = self._ics.data
|
# All the rows, with their deleted flag: the generation marks the
|
||||||
|
# previous rows as deleted (clear_data)
|
||||||
|
self._copy = list(self._ics._data)
|
||||||
|
self._deleted = [d.is_deleted() for d in self._copy]
|
||||||
self._generator = generator
|
self._generator = generator
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
self._ics.data = self._copy
|
for data, deleted in zip(self._copy, self._deleted):
|
||||||
|
if deleted:
|
||||||
|
data.set_as_deleted()
|
||||||
|
else:
|
||||||
|
data.set_as_not_deleted()
|
||||||
|
self._ics.data = list(self._copy)
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
if self._generator == "growing":
|
if self._generator == "growing":
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ class SetDataCommand(QUndoCommand):
|
|||||||
self._data._set_i_c_v(self._index, self._column, self._new)
|
self._data._set_i_c_v(self._index, self._column, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class SetMetaDataCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._column = column
|
||||||
|
self._old = getattr(self._data, self._column)
|
||||||
|
self._new = float(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
setattr(self._data, self._column, self._old)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
setattr(self._data, self._column, self._new)
|
||||||
|
|
||||||
|
|
||||||
class AddCommand(QUndoCommand):
|
class AddCommand(QUndoCommand):
|
||||||
def __init__(self, data, index):
|
def __init__(self, data, index):
|
||||||
QUndoCommand.__init__(self)
|
QUndoCommand.__init__(self)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (
|
|||||||
QDialogButtonBox, QPushButton, QLineEdit,
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
QFileDialog, QTableView, QAbstractItemView,
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
QUndoStack, QShortcut, QAction, QItemDelegate,
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
QHeaderView,
|
QHeaderView, QVBoxLayout,
|
||||||
)
|
)
|
||||||
|
|
||||||
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
@@ -44,6 +44,8 @@ from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
|||||||
from View.LateralContribution.Edit.translate import LCETranslate
|
from View.LateralContribution.Edit.translate import LCETranslate
|
||||||
from View.LateralContribution.Edit.Table import TableModel
|
from View.LateralContribution.Edit.Table import TableModel
|
||||||
from View.LateralContribution.Edit.Plot import Plot
|
from View.LateralContribution.Edit.Plot import Plot
|
||||||
|
from View.LateralContribution.Edit.UndoCommand import SetMetaDataCommand
|
||||||
|
from View.BoundaryCondition.Edit.Window import WD50Sigma
|
||||||
|
|
||||||
_translate = QCoreApplication.translate
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
@@ -54,10 +56,15 @@ class EditLateralContributionWindow(PamhyrWindow):
|
|||||||
|
|
||||||
def __init__(self, data=None,
|
def __init__(self, data=None,
|
||||||
study=None, config=None,
|
study=None, config=None,
|
||||||
parent=None):
|
tab="liquid", parent=None):
|
||||||
self._data = data
|
self._data = data
|
||||||
|
# Grain size of the sediment brought: solid contributions only
|
||||||
|
self._is_solid = tab == "solid"
|
||||||
|
|
||||||
trad = LCETranslate()
|
trad = LCETranslate()
|
||||||
|
if self._is_solid:
|
||||||
|
trad.get_dict("table_headers")["discharge"] = \
|
||||||
|
trad["solid_discharge"]
|
||||||
name = trad[self._pamhyr_name]
|
name = trad[self._pamhyr_name]
|
||||||
if self._data is not None:
|
if self._data is not None:
|
||||||
edge_name = (self._data.reach.name if self._data.reach is not None
|
edge_name = (self._data.reach.name if self._data.reach is not None
|
||||||
@@ -81,6 +88,7 @@ class EditLateralContributionWindow(PamhyrWindow):
|
|||||||
|
|
||||||
self.setup_table()
|
self.setup_table()
|
||||||
self.setup_plot()
|
self.setup_plot()
|
||||||
|
self.setup_d50sigma()
|
||||||
self.setup_connections()
|
self.setup_connections()
|
||||||
|
|
||||||
def setup_table(self):
|
def setup_table(self):
|
||||||
@@ -139,7 +147,18 @@ class EditLateralContributionWindow(PamhyrWindow):
|
|||||||
)
|
)
|
||||||
self.plot.draw()
|
self.plot.draw()
|
||||||
|
|
||||||
|
def setup_d50sigma(self):
|
||||||
|
if not self._is_solid:
|
||||||
|
return
|
||||||
|
|
||||||
|
layout = self.find(QVBoxLayout, "verticalLayout_table")
|
||||||
|
self._d50sigma = WD50Sigma(parent=self)
|
||||||
|
layout.addWidget(self._d50sigma)
|
||||||
|
self.widget_update()
|
||||||
|
|
||||||
def setup_connections(self):
|
def setup_connections(self):
|
||||||
|
self._undo_stack.indexChanged.connect(self._propagate_lateral_update)
|
||||||
|
|
||||||
if self._study.is_editable():
|
if self._study.is_editable():
|
||||||
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)
|
||||||
@@ -148,10 +167,37 @@ class EditLateralContributionWindow(PamhyrWindow):
|
|||||||
self._table.dataChanged.connect(self.update)
|
self._table.dataChanged.connect(self.update)
|
||||||
self._table.layoutChanged.connect(self.update)
|
self._table.layoutChanged.connect(self.update)
|
||||||
|
|
||||||
|
if self._is_solid:
|
||||||
|
if self._study.is_editable():
|
||||||
|
self._d50sigma.d50Changed.connect(self.d50_changed)
|
||||||
|
self._d50sigma.sigmaChanged.connect(self.sigma_changed)
|
||||||
|
else:
|
||||||
|
self._d50sigma.setEnabled(False)
|
||||||
|
|
||||||
|
def _propagate_lateral_update(self, index=None):
|
||||||
|
# Every change of the lateral contributions goes through the undo
|
||||||
|
# stack (push, undo, redo): the main window then reruns their
|
||||||
|
# checkers
|
||||||
|
self._propagate_update(key=Modules.LATERAL_CONTRIBUTION)
|
||||||
|
|
||||||
|
def d50_changed(self, value):
|
||||||
|
self._undo_stack.push(SetMetaDataCommand(self._data, "d50", value))
|
||||||
|
|
||||||
|
def sigma_changed(self, value):
|
||||||
|
self._undo_stack.push(
|
||||||
|
SetMetaDataCommand(self._data, "sigma", value)
|
||||||
|
)
|
||||||
|
|
||||||
|
def widget_update(self):
|
||||||
|
if self._is_solid:
|
||||||
|
self._d50sigma.set_d50(self._data.d50)
|
||||||
|
self._d50sigma.set_sigma(self._data.sigma)
|
||||||
|
|
||||||
def _propagated_update(self, key=Modules(0)):
|
def _propagated_update(self, key=Modules(0)):
|
||||||
if Modules.STUDY in key:
|
if Modules.STUDY in key:
|
||||||
self._table.layoutChanged.emit()
|
self._table.layoutChanged.emit()
|
||||||
self.plot.update()
|
self.plot.update()
|
||||||
|
self.widget_update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.plot.update()
|
self.plot.update()
|
||||||
@@ -235,7 +281,9 @@ class EditLateralContributionWindow(PamhyrWindow):
|
|||||||
def _undo(self):
|
def _undo(self):
|
||||||
self._table.undo()
|
self._table.undo()
|
||||||
self.plot.update()
|
self.plot.update()
|
||||||
|
self.widget_update()
|
||||||
|
|
||||||
def _redo(self):
|
def _redo(self):
|
||||||
self._table.redo()
|
self._table.redo()
|
||||||
self.plot.update()
|
self.plot.update()
|
||||||
|
self.widget_update()
|
||||||
@@ -39,3 +39,7 @@ class LCETranslate(LCTranslate):
|
|||||||
"discharge": self._dict["unit_discharge"],
|
"discharge": self._dict["unit_discharge"],
|
||||||
"z": self._dict["unit_elevation"],
|
"z": self._dict["unit_elevation"],
|
||||||
}
|
}
|
||||||
|
# Discharge column of a solid contribution
|
||||||
|
self._dict["solid_discharge"] = _translate(
|
||||||
|
"LateralContribution", "Solid discharge (kg/s)"
|
||||||
|
)
|
||||||
@@ -20,6 +20,7 @@ import logging
|
|||||||
|
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Modules import Modules
|
||||||
from View.Tools.PamhyrWindow import PamhyrWindow
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
@@ -67,6 +68,7 @@ logger = logging.getLogger()
|
|||||||
class LateralContributionWindow(PamhyrWindow):
|
class LateralContributionWindow(PamhyrWindow):
|
||||||
_pamhyr_ui = "LateralContributions"
|
_pamhyr_ui = "LateralContributions"
|
||||||
_pamhyr_name = "Lateral contribution"
|
_pamhyr_name = "Lateral contribution"
|
||||||
|
_tabs = ["liquid", "solid"]
|
||||||
|
|
||||||
def __init__(self, study=None, config=None, parent=None):
|
def __init__(self, study=None, config=None, parent=None):
|
||||||
trad = LCTranslate()
|
trad = LCTranslate()
|
||||||
@@ -96,7 +98,7 @@ class LateralContributionWindow(PamhyrWindow):
|
|||||||
else:
|
else:
|
||||||
editable_headers = []
|
editable_headers = []
|
||||||
|
|
||||||
for t in ["liquid", "solid", "suspenssion"]:
|
for t in self._tabs:
|
||||||
self._delegate_type = ComboBoxDelegate(
|
self._delegate_type = ComboBoxDelegate(
|
||||||
data=self._study.river,
|
data=self._study.river,
|
||||||
mode="type",
|
mode="type",
|
||||||
@@ -158,6 +160,8 @@ class LateralContributionWindow(PamhyrWindow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def setup_connections(self):
|
def setup_connections(self):
|
||||||
|
self._undo_stack.indexChanged.connect(self._propagate_lateral_update)
|
||||||
|
|
||||||
if self._study.is_editable():
|
if self._study.is_editable():
|
||||||
self.find(QAction, "action_import").triggered.connect(
|
self.find(QAction, "action_import").triggered.connect(
|
||||||
self.import_from_file)
|
self.import_from_file)
|
||||||
@@ -168,7 +172,7 @@ class LateralContributionWindow(PamhyrWindow):
|
|||||||
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
||||||
self.tabs.currentChanged.connect(self._set_current_reach)
|
self.tabs.currentChanged.connect(self._set_current_reach)
|
||||||
|
|
||||||
for t in ["liquid", "solid", "suspenssion"]:
|
for t in self._tabs:
|
||||||
table = self.find(QTableView, f"tableView_{t}")
|
table = self.find(QTableView, f"tableView_{t}")
|
||||||
table.selectionModel()\
|
table.selectionModel()\
|
||||||
.selectionChanged\
|
.selectionChanged\
|
||||||
@@ -177,6 +181,12 @@ class LateralContributionWindow(PamhyrWindow):
|
|||||||
self._table[t].dataChanged\
|
self._table[t].dataChanged\
|
||||||
.connect(self._set_current_reach)
|
.connect(self._set_current_reach)
|
||||||
|
|
||||||
|
def _propagate_lateral_update(self, index=None):
|
||||||
|
# Every change of the lateral contributions goes through the undo
|
||||||
|
# stack (push, undo, redo): the main window then reruns their
|
||||||
|
# checkers
|
||||||
|
self._propagate_update(key=Modules.LATERAL_CONTRIBUTION)
|
||||||
|
|
||||||
def current_tab(self):
|
def current_tab(self):
|
||||||
return self.find(QTabWidget, "tabWidget")\
|
return self.find(QTabWidget, "tabWidget")\
|
||||||
.currentWidget()\
|
.currentWidget()\
|
||||||
@@ -303,6 +313,7 @@ class LateralContributionWindow(PamhyrWindow):
|
|||||||
win = EditLateralContributionWindow(
|
win = EditLateralContributionWindow(
|
||||||
data=data,
|
data=data,
|
||||||
study=self._study,
|
study=self._study,
|
||||||
|
tab=tab,
|
||||||
parent=self
|
parent=self
|
||||||
)
|
)
|
||||||
win.show()
|
win.show()
|
||||||
|
|||||||
@@ -30,7 +30,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 RubarBE
|
||||||
from tools import logger_exception, pamhyr_db_need_update, timer
|
from tools import logger_exception, pamhyr_db_need_update, timer
|
||||||
|
|
||||||
from PyQt5 import QtGui
|
from PyQt5 import QtGui
|
||||||
@@ -2082,7 +2082,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
|
dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
|
||||||
dialog.setDefaultSuffix(".BIN")
|
dialog.setDefaultSuffix(".BIN")
|
||||||
# dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
|
# dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
|
||||||
dialog.setNameFilters(['Mage (*.BIN)', 'Rubar3 (profil.*)'])
|
dialog.setNameFilters(['Mage (*.BIN)', 'RubarBE (profil.*)'])
|
||||||
|
|
||||||
if self._study.filename is not None:
|
if self._study.filename is not None:
|
||||||
if self._last_solver is None:
|
if self._last_solver is None:
|
||||||
@@ -2102,7 +2102,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
if ".BIN" in file_name[0]:
|
if ".BIN" in file_name[0]:
|
||||||
solver = Mage8("Mage8")
|
solver = Mage8("Mage8")
|
||||||
else:
|
else:
|
||||||
solver = Rubar3("Rubar3")
|
solver = RubarBE("RubarBE")
|
||||||
|
|
||||||
self.open_solver_results(
|
self.open_solver_results(
|
||||||
solver,
|
solver,
|
||||||
|
|||||||
@@ -192,11 +192,37 @@ class RunSolverTranslate(PamhyrTranslate):
|
|||||||
"Lateral contribution {name} on reach {reach} has no value: it "
|
"Lateral contribution {name} on reach {reach} has no value: it "
|
||||||
"is ignored."
|
"is ignored."
|
||||||
)
|
)
|
||||||
self._dict["lateral_solid_unsupported"] = _translate(
|
self._dict["lateral_solid_no_transport"] = _translate(
|
||||||
"Solver",
|
"Solver",
|
||||||
"Export cancelled: solid or suspension lateral contribution "
|
"Solid lateral contribution {name} on reach {reach} is not taken "
|
||||||
"{name} on reach {reach}: RubarBE only reads the sediment of a "
|
"into account: sediment transport (rubarbe_trased) is disabled "
|
||||||
"liquid inflow, which Pamhyr2 cannot link to it. Remove it."
|
"in the solver parameters."
|
||||||
|
)
|
||||||
|
self._dict["lateral_solid_unmatched"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Solid lateral contribution {name} on reach {reach} is not taken "
|
||||||
|
"into account: RubarBE brings sediment only with water, and no "
|
||||||
|
"liquid lateral contribution has the same sections."
|
||||||
|
)
|
||||||
|
self._dict["lateral_solid_unisol"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: solid lateral contribution {name} on reach "
|
||||||
|
"{reach} gives a solid discharge in kg/s, but the solver "
|
||||||
|
"parameters set the solid unit (rubarbe_sediment_unisol) to a "
|
||||||
|
"concentration. Set it to a mass discharge (kg/s)."
|
||||||
|
)
|
||||||
|
self._dict["lateral_solid_invalid"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: solid lateral contribution {name} on reach "
|
||||||
|
"{reach} must have D50 > 0 m and sigma >= 1 (1 for a uniform "
|
||||||
|
"sediment)."
|
||||||
|
)
|
||||||
|
self._dict["lateral_solid_conflict"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: solid lateral contributions {names} on reach "
|
||||||
|
"{reach} are attached to the same liquid contribution {liquid} "
|
||||||
|
"but have different D50 or sigma. RubarBE accepts a single "
|
||||||
|
"grain size per inflow."
|
||||||
)
|
)
|
||||||
self._dict["lateral_iodev_disabled"] = _translate(
|
self._dict["lateral_iodev_disabled"] = _translate(
|
||||||
"Solver",
|
"Solver",
|
||||||
|
|||||||
@@ -35,7 +35,13 @@
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QTableView" name="tableView"/>
|
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_table">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="verticalLayoutWidget">
|
<widget class="QWidget" name="verticalLayoutWidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -59,16 +59,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_suspenssion">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Suspension</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QGridLayout" name="gridLayout_4">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QTableView" name="tableView_suspenssion"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="verticalLayoutWidget">
|
<widget class="QWidget" name="verticalLayoutWidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||||
|
|||||||
+1
-17
@@ -428,15 +428,7 @@ class Config(SQL):
|
|||||||
new._cmd_solver = f"\"{path}{ext}\" @args @input"
|
new._cmd_solver = f"\"{path}{ext}\" @args @input"
|
||||||
self._solvers.append(new)
|
self._solvers.append(new)
|
||||||
|
|
||||||
# Rubar3
|
# RubarBE
|
||||||
ctor = solver_type_list["rubar3"]
|
|
||||||
new = ctor("default-Rubar3")
|
|
||||||
new._description = "Default Pamhyr2 Rubar 3 version"
|
|
||||||
path = os.path.join("@install_dir", "rubar", f"rubarbe{ext}")
|
|
||||||
new._cmd_solver = f""""{path}" @args @input"""
|
|
||||||
self._solvers.append(new)
|
|
||||||
|
|
||||||
# RubarBE (same executable as Rubar3, see notice §10.3)
|
|
||||||
ctor = solver_type_list["rubarbe"]
|
ctor = solver_type_list["rubarbe"]
|
||||||
new = ctor("default-RubarBE")
|
new = ctor("default-RubarBE")
|
||||||
new._description = "Default Pamhyr2 RubarBE version"
|
new._description = "Default Pamhyr2 RubarBE version"
|
||||||
@@ -444,14 +436,6 @@ class Config(SQL):
|
|||||||
new._cmd_solver = f""""{path}" @args @input"""
|
new._cmd_solver = f""""{path}" @args @input"""
|
||||||
self._solvers.append(new)
|
self._solvers.append(new)
|
||||||
|
|
||||||
# Mage fake 7
|
|
||||||
# ctor = solver_type_list["mage_fake7"]
|
|
||||||
# new = ctor("default-mage-fake-7")
|
|
||||||
# new._description = "Default Pamhyr2 mage fake version 7"
|
|
||||||
# new._cmd_solver = f"""\
|
|
||||||
# "@install_dir/mage8/mage_as7{ext}" @args @input"""
|
|
||||||
# self._solvers.append(new)
|
|
||||||
|
|
||||||
# Backup
|
# Backup
|
||||||
self.backup_enable = True
|
self.backup_enable = True
|
||||||
# self.backup_path = ""
|
# self.backup_path = ""
|
||||||
|
|||||||
Reference in new issue
Block a user