mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-29 19:10:42 +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 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 -*-
|
||||
|
||||
import queue
|
||||
import types
|
||||
import random
|
||||
import logging
|
||||
import numpy as np
|
||||
@@ -842,7 +843,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
return poly
|
||||
|
||||
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 = [left] + points + [right]
|
||||
|
||||
@@ -224,6 +224,9 @@ class LateralContribution(SQLSubModel):
|
||||
self._header = []
|
||||
self._types = [float, float]
|
||||
|
||||
self._d50 = 0.002
|
||||
self._sigma = 1.0
|
||||
|
||||
@classmethod
|
||||
def _db_create(cls, execute, ext=""):
|
||||
execute(f"""
|
||||
@@ -236,6 +239,8 @@ class LateralContribution(SQLSubModel):
|
||||
reach INTEGER,
|
||||
begin_section INTEGER,
|
||||
end_section INTEGER,
|
||||
d50 REAL DEFAULT 0.002,
|
||||
sigma REAL DEFAULT 1,
|
||||
{Scenario.create_db_add_scenario()},
|
||||
{Scenario.create_db_add_scenario_fk()},
|
||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
||||
@@ -286,8 +291,32 @@ class LateralContribution(SQLSubModel):
|
||||
"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)
|
||||
|
||||
@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
|
||||
def _db_update_to_0_2_0(cls, execute, data):
|
||||
table = "lateral_contribution"
|
||||
@@ -339,7 +368,7 @@ class LateralContribution(SQLSubModel):
|
||||
|
||||
table = execute(
|
||||
"SELECT pamhyr_id, deleted, name, type, " +
|
||||
"reach, begin_section, end_section, scenario " +
|
||||
"reach, begin_section, end_section, d50, sigma, scenario " +
|
||||
"FROM lateral_contribution " +
|
||||
f"WHERE tab = '{tab}'" +
|
||||
f"AND scenario = {scenario.id} " +
|
||||
@@ -356,6 +385,8 @@ class LateralContribution(SQLSubModel):
|
||||
reach = next(it)
|
||||
b_section = next(it)
|
||||
e_section = next(it)
|
||||
d50 = next(it)
|
||||
sigma = next(it)
|
||||
owner_scenario = next(it)
|
||||
|
||||
ctor = cls._get_ctor_from_type(t)
|
||||
@@ -367,6 +398,10 @@ class LateralContribution(SQLSubModel):
|
||||
lc.reach = None
|
||||
lc._begin_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:
|
||||
lc.reach = next(
|
||||
@@ -426,12 +461,13 @@ class LateralContribution(SQLSubModel):
|
||||
"INSERT INTO " +
|
||||
"lateral_contribution(" +
|
||||
"pamhyr_id, deleted, name, type, tab, " +
|
||||
"reach, begin_section, end_section, scenario) " +
|
||||
"reach, begin_section, end_section, d50, sigma, scenario) " +
|
||||
"VALUES (" +
|
||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||
f"'{self._db_format(self._name)}', " +
|
||||
f"'{self._db_format(self._type)}', '{tab}', {reach}, " +
|
||||
f"{begin_section}, {end_section}, " +
|
||||
f"{self._d50}, {self._sigma}, " +
|
||||
f"{self._status.scenario_id}" +
|
||||
")"
|
||||
)
|
||||
@@ -523,10 +559,30 @@ class LateralContribution(SQLSubModel):
|
||||
new._reach = reach
|
||||
new._begin_section = begin_section
|
||||
new._end_section = end_section
|
||||
new._d50 = self._d50
|
||||
new._sigma = self._sigma
|
||||
new._data = [data.cloned() for data in self.data]
|
||||
new.modified()
|
||||
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
|
||||
def begin_rk(self):
|
||||
if self._begin_section is None:
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
from copy import copy
|
||||
from tools import trace, timer
|
||||
from Solver.Solvers import solver_type_list
|
||||
from Solver.RubarBE import Rubar3
|
||||
|
||||
from Model.Scenario import Scenario
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
@@ -296,14 +297,20 @@ class SolverParametersList(PamhyrModelList):
|
||||
scenario = data["scenario"]
|
||||
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 = {}
|
||||
|
||||
if scenario is None:
|
||||
return new
|
||||
|
||||
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:
|
||||
continue
|
||||
|
||||
@@ -327,6 +334,32 @@ class SolverParametersList(PamhyrModelList):
|
||||
|
||||
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):
|
||||
t = self._solver._type
|
||||
execute(
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ logger = logging.getLogger()
|
||||
|
||||
|
||||
class Study(SQLModel):
|
||||
_version = "0.2.10"
|
||||
_version = "0.2.11"
|
||||
|
||||
_sub_classes = [
|
||||
Scenario,
|
||||
|
||||
+3
-1
@@ -28,6 +28,7 @@ from tools import timer, trace, logger_exception
|
||||
from Solver.CommandLine import CommandLineSolver
|
||||
from Checker.Mage import (
|
||||
MageNetworkGraphChecker, MageGeometryGuideLineChecker,
|
||||
MageLateralSolidChecker,
|
||||
)
|
||||
|
||||
from Model.Results.Results import Results
|
||||
@@ -106,6 +107,7 @@ class Mage(CommandLineSolver):
|
||||
MageNetworkGraphChecker(connectivity=True, version=cls._type),
|
||||
MageNetworkGraphChecker(connectivity=False, version=cls._type),
|
||||
MageGeometryGuideLineChecker(version=cls._type),
|
||||
MageLateralSolidChecker(version=cls._type),
|
||||
]
|
||||
|
||||
return lst
|
||||
@@ -391,7 +393,7 @@ class Mage(CommandLineSolver):
|
||||
qlog.put(f"Export LAT file")
|
||||
files.append(f"{name}.LAT")
|
||||
|
||||
for tab in ["liquid", "solid", "suspenssion"]:
|
||||
for tab in ["liquid"]:
|
||||
for lateral in lst.get_tab(tab):
|
||||
self._export_LC(study, lateral, f, qlog)
|
||||
|
||||
|
||||
+122
-40
@@ -123,10 +123,21 @@ class Rubar3(CommandLineSolver):
|
||||
|
||||
@classmethod
|
||||
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 #
|
||||
@@ -154,19 +165,11 @@ class Rubar3(CommandLineSolver):
|
||||
name = study.name.replace(" ", "_")
|
||||
|
||||
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"
|
||||
|
||||
# Check the hydraulic data before writing any file
|
||||
try:
|
||||
self._check_regime(values)
|
||||
hydraulics = self._hydraulic_data(study, values)
|
||||
geometry = self._geometry_data(hydraulics["edge"], trased)
|
||||
solid = self._solid_boundary(
|
||||
@@ -174,7 +177,8 @@ class Rubar3(CommandLineSolver):
|
||||
hydraulics["warnings"])
|
||||
saves = self._save_data(values, hydraulics)
|
||||
laterals = self._lateral_data(
|
||||
study.river, hydraulics, values, hydraulics["warnings"])
|
||||
study.river, hydraulics, values, trased,
|
||||
hydraulics["warnings"])
|
||||
structures = self._structure_data(
|
||||
study.river, hydraulics, hydraulics["warnings"])
|
||||
except RubarExportError as error:
|
||||
@@ -231,13 +235,20 @@ class Rubar3(CommandLineSolver):
|
||||
# NCLMAX (variables.f90): points of all the solid hydrographs
|
||||
_max_solid_points = 1000
|
||||
|
||||
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"""
|
||||
trad = RunSolverTranslate()
|
||||
river = study.river
|
||||
@staticmethod
|
||||
def _check_regime(values):
|
||||
"""REGIME=1 imposes the water depth upstream, which a reflecting
|
||||
upstream boundary (CONDAM=0) cannot do"""
|
||||
if (values.get("rubarbe_regime") == "1" and
|
||||
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()
|
||||
if len(edges) != 1:
|
||||
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:])):
|
||||
raise RubarExportError(trad["rk_not_increasing"].format(
|
||||
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 = []
|
||||
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
|
||||
_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,
|
||||
init2). Return [(first cell, last cell, [(t, q), ...]), ...]
|
||||
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
|
||||
section). The first and last cells of the reach are never used,
|
||||
as RubarBE requires. With IODEV=N, the contributions are ignored
|
||||
with a warning. Otherwise, solid and suspension contributions have
|
||||
no RubarBE equivalent (their sediment cannot be linked to an
|
||||
inflow): they are refused"""
|
||||
with a warning.
|
||||
|
||||
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()
|
||||
edge = hydraulics["edge"]
|
||||
profiles = hydraulics["profiles"]
|
||||
@@ -580,40 +607,50 @@ class Rubar3(CommandLineSolver):
|
||||
return kept
|
||||
|
||||
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
|
||||
# without them
|
||||
if not iodev:
|
||||
for lc in lcs + sediment:
|
||||
for lc in lcs + solids:
|
||||
warnings.append(trad["lateral_iodev_disabled"].format(
|
||||
name=lc.name, reach=edge.name))
|
||||
return None
|
||||
|
||||
if sediment:
|
||||
raise RubarExportError(trad["lateral_solid_unsupported"].format(
|
||||
name=sediment[0].name, reach=edge.name))
|
||||
if not trased:
|
||||
for lc in solids:
|
||||
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)}
|
||||
count = len(profiles)
|
||||
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))
|
||||
|
||||
def zone_and_data(lc):
|
||||
if (id(lc.begin_section) not in index or
|
||||
id(lc.end_section) not in index):
|
||||
raise RubarExportError(trad["lateral_sections"].format(
|
||||
name=lc.name, reach=edge.name))
|
||||
b, e = sorted((index[id(lc.begin_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]
|
||||
times = [t for t, _ in data]
|
||||
if any(t1 < t0 for t0, t1 in zip(times, times[1:])):
|
||||
raise RubarExportError(trad["lateral_not_sorted"].format(
|
||||
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]
|
||||
blocks.append((first, last, [(t, sign * q) for t, q in data],
|
||||
lc.name))
|
||||
@@ -625,13 +662,56 @@ class Rubar3(CommandLineSolver):
|
||||
raise RubarExportError(trad["lateral_overlap"].format(
|
||||
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)
|
||||
for first, last, data, _ in blocks)
|
||||
for first, last, data, _, _ in result)
|
||||
if points > self._max_lateral_points:
|
||||
raise RubarExportError(trad["lateral_too_long"].format(
|
||||
count=points, maximum=self._max_lateral_points))
|
||||
|
||||
return [block[:3] for block in blocks]
|
||||
return result
|
||||
|
||||
##############
|
||||
# Structures #
|
||||
@@ -1251,13 +1331,15 @@ class Rubar3(CommandLineSolver):
|
||||
if qlog is not None:
|
||||
qlog.put("Export HYDEVS file")
|
||||
lines = []
|
||||
for first, last, data in laterals:
|
||||
for first, last, data, (d50, sigma), length in laterals:
|
||||
if trased:
|
||||
lines.append(f"{first} {last} {len(data)} 0.0 0.0\n")
|
||||
lines += [f"{q!r} {t!r} 0.0 {t!r}\n" for t, q in data]
|
||||
lines.append(
|
||||
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:
|
||||
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)
|
||||
|
||||
def _export_ouvrag(self, structures, repertory, qlog, name="0"):
|
||||
|
||||
@@ -36,7 +36,6 @@ solver_long_name = {
|
||||
"adistswc": "Adis-TS_WC",
|
||||
"adisttwc": "Adis-TT_WC",
|
||||
"rubarbe": "RubarBE",
|
||||
"rubar3": "Rubar3",
|
||||
}
|
||||
|
||||
solver_type_list = {
|
||||
@@ -47,5 +46,4 @@ solver_type_list = {
|
||||
"adistswc": AdisTSwc,
|
||||
"adisttwc": AdisTTwc,
|
||||
"rubarbe": RubarBE,
|
||||
"rubar3": Rubar3,
|
||||
}
|
||||
@@ -188,7 +188,10 @@ class EditBoundaryConditionWindow(PamhyrWindow):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
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.canvas)
|
||||
|
||||
@@ -105,9 +105,19 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
strickler
|
||||
)
|
||||
elif self._mode == "rk":
|
||||
self.editor.addItems(
|
||||
list(map(str, self._data.reach.get_rk()))
|
||||
)
|
||||
if self._data is None:
|
||||
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)))
|
||||
return self.editor
|
||||
|
||||
@@ -42,7 +42,7 @@ class PlotAC(PamhyrPlot):
|
||||
self._isometric_axis = False
|
||||
|
||||
self._auto_relim_update = True
|
||||
self._autoscale_update = False
|
||||
self._autoscale_update = True
|
||||
|
||||
@property
|
||||
def river(self):
|
||||
|
||||
@@ -31,6 +31,14 @@ _translate = QCoreApplication.translate
|
||||
|
||||
|
||||
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,
|
||||
river=None, reach=None, profile=None,
|
||||
parent=None):
|
||||
@@ -134,19 +142,14 @@ class PlotRKC(PamhyrPlot):
|
||||
vymin = []
|
||||
vymax = []
|
||||
self.anotate_lst = []
|
||||
self._hs_drawn = []
|
||||
hs_color = []
|
||||
index = self.parent.tableView.selectedIndexes()
|
||||
for i, hs in enumerate(lhs):
|
||||
for hs in lhs:
|
||||
section = hs.input_section
|
||||
if section is None:
|
||||
continue
|
||||
|
||||
if i == index[0].row():
|
||||
color = "blue"
|
||||
elif hs.enabled:
|
||||
color = "red"
|
||||
else:
|
||||
color = "darkgrey"
|
||||
color = self._hs_color(hs)
|
||||
|
||||
x = section.rk
|
||||
if x is not None:
|
||||
@@ -164,6 +167,7 @@ class PlotRKC(PamhyrPlot):
|
||||
vymin.append(min(z_min))
|
||||
vymax.append(max(z_max))
|
||||
hs_color.append(color)
|
||||
self._hs_drawn.append(hs)
|
||||
|
||||
self.hs_vlines = self.canvas.axes.vlines(
|
||||
x=vx, ymin=vymin, ymax=vymax,
|
||||
@@ -172,6 +176,28 @@ class PlotRKC(PamhyrPlot):
|
||||
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):
|
||||
|
||||
reach = self._current_reach
|
||||
@@ -255,7 +281,8 @@ class PlotRKC(PamhyrPlot):
|
||||
|
||||
closest = self._closest_profile(event)
|
||||
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)
|
||||
|
||||
return
|
||||
|
||||
@@ -130,11 +130,13 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
self._checkbox = self.find(QCheckBox, f"checkBox")
|
||||
self._set_checkbox_state()
|
||||
|
||||
_toolbar_items = ["home", "move", "zoom", "save", "iso", "back/forward"]
|
||||
|
||||
def setup_plots(self):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
self.toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self
|
||||
self.canvas, self, items=self._toolbar_items
|
||||
)
|
||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||
self.plot_layout.addWidget(self.toolbar)
|
||||
@@ -143,7 +145,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
self.plot_rkc = PlotRKC(
|
||||
canvas=self.canvas,
|
||||
river=self._study.river,
|
||||
reach=None,
|
||||
reach=self._displayed_reach(None),
|
||||
profile=None,
|
||||
trad=self._trad,
|
||||
toolbar=self.toolbar,
|
||||
@@ -154,7 +156,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_2.setObjectName("canvas_2")
|
||||
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.addWidget(self.toolbar_2)
|
||||
@@ -183,7 +185,7 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
table = self.find(QTableView, "tableView")
|
||||
table.selectionModel()\
|
||||
.selectionChanged\
|
||||
.connect(self.update)
|
||||
.connect(self.update_selection)
|
||||
|
||||
self._table.dataChanged.connect(self.update)
|
||||
self._table.layoutChanged.connect(self.update)
|
||||
@@ -283,10 +285,17 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
)
|
||||
|
||||
def update(self):
|
||||
"""Data changed: plots redrawn, fitted to their data"""
|
||||
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()
|
||||
|
||||
if (len(rows) == 0 or
|
||||
|
||||
@@ -384,9 +384,19 @@ class InitialConditionTableModel(PamhyrTableModel):
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def generate(self, generator, param, option):
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
self._undo.beginMacro(f"generate {generator}")
|
||||
self._undo.push(
|
||||
GenerateCommand(
|
||||
self._lst, generator, param, option
|
||||
)
|
||||
)
|
||||
self._undo.push(
|
||||
SortCommand(
|
||||
self._lst, False
|
||||
)
|
||||
)
|
||||
self._undo.endMacro()
|
||||
|
||||
self.layoutChanged.emit()
|
||||
@@ -100,15 +100,19 @@ class SortCommand(QUndoCommand):
|
||||
self._indexes = None
|
||||
|
||||
def undo(self):
|
||||
ll = self._ics.data
|
||||
order = {
|
||||
id(x): self._indexes[i]
|
||||
for i, x in enumerate(self._ics.data)
|
||||
}
|
||||
self._ics.sort(
|
||||
key=lambda x: self._indexes[ll.index(x)]
|
||||
key=lambda x: order.get(id(x), len(order))
|
||||
)
|
||||
|
||||
def redo(self):
|
||||
self._ics.sort(
|
||||
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:
|
||||
self._indexes = list(
|
||||
@@ -182,11 +186,19 @@ class GenerateCommand(QUndoCommand):
|
||||
self._ics = ics
|
||||
self._param = param
|
||||
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
|
||||
|
||||
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):
|
||||
if self._generator == "growing":
|
||||
|
||||
@@ -44,6 +44,22 @@ class SetDataCommand(QUndoCommand):
|
||||
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):
|
||||
def __init__(self, data, index):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (
|
||||
QDialogButtonBox, QPushButton, QLineEdit,
|
||||
QFileDialog, QTableView, QAbstractItemView,
|
||||
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||
QHeaderView,
|
||||
QHeaderView, QVBoxLayout,
|
||||
)
|
||||
|
||||
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.Table import TableModel
|
||||
from View.LateralContribution.Edit.Plot import Plot
|
||||
from View.LateralContribution.Edit.UndoCommand import SetMetaDataCommand
|
||||
from View.BoundaryCondition.Edit.Window import WD50Sigma
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
@@ -54,10 +56,15 @@ class EditLateralContributionWindow(PamhyrWindow):
|
||||
|
||||
def __init__(self, data=None,
|
||||
study=None, config=None,
|
||||
parent=None):
|
||||
tab="liquid", parent=None):
|
||||
self._data = data
|
||||
# Grain size of the sediment brought: solid contributions only
|
||||
self._is_solid = tab == "solid"
|
||||
|
||||
trad = LCETranslate()
|
||||
if self._is_solid:
|
||||
trad.get_dict("table_headers")["discharge"] = \
|
||||
trad["solid_discharge"]
|
||||
name = trad[self._pamhyr_name]
|
||||
if self._data 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_plot()
|
||||
self.setup_d50sigma()
|
||||
self.setup_connections()
|
||||
|
||||
def setup_table(self):
|
||||
@@ -139,7 +147,18 @@ class EditLateralContributionWindow(PamhyrWindow):
|
||||
)
|
||||
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):
|
||||
self._undo_stack.indexChanged.connect(self._propagate_lateral_update)
|
||||
|
||||
if self._study.is_editable():
|
||||
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||
self.find(QAction, "action_del").triggered.connect(self.delete)
|
||||
@@ -148,10 +167,37 @@ class EditLateralContributionWindow(PamhyrWindow):
|
||||
self._table.dataChanged.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)):
|
||||
if Modules.STUDY in key:
|
||||
self._table.layoutChanged.emit()
|
||||
self.plot.update()
|
||||
self.widget_update()
|
||||
|
||||
def update(self):
|
||||
self.plot.update()
|
||||
@@ -235,7 +281,9 @@ class EditLateralContributionWindow(PamhyrWindow):
|
||||
def _undo(self):
|
||||
self._table.undo()
|
||||
self.plot.update()
|
||||
self.widget_update()
|
||||
|
||||
def _redo(self):
|
||||
self._table.redo()
|
||||
self.plot.update()
|
||||
self.widget_update()
|
||||
@@ -39,3 +39,7 @@ class LCETranslate(LCTranslate):
|
||||
"discharge": self._dict["unit_discharge"],
|
||||
"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 Modules import Modules
|
||||
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
@@ -67,6 +68,7 @@ logger = logging.getLogger()
|
||||
class LateralContributionWindow(PamhyrWindow):
|
||||
_pamhyr_ui = "LateralContributions"
|
||||
_pamhyr_name = "Lateral contribution"
|
||||
_tabs = ["liquid", "solid"]
|
||||
|
||||
def __init__(self, study=None, config=None, parent=None):
|
||||
trad = LCTranslate()
|
||||
@@ -96,7 +98,7 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
else:
|
||||
editable_headers = []
|
||||
|
||||
for t in ["liquid", "solid", "suspenssion"]:
|
||||
for t in self._tabs:
|
||||
self._delegate_type = ComboBoxDelegate(
|
||||
data=self._study.river,
|
||||
mode="type",
|
||||
@@ -158,6 +160,8 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
)
|
||||
|
||||
def setup_connections(self):
|
||||
self._undo_stack.indexChanged.connect(self._propagate_lateral_update)
|
||||
|
||||
if self._study.is_editable():
|
||||
self.find(QAction, "action_import").triggered.connect(
|
||||
self.import_from_file)
|
||||
@@ -168,7 +172,7 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
||||
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.selectionModel()\
|
||||
.selectionChanged\
|
||||
@@ -177,6 +181,12 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
self._table[t].dataChanged\
|
||||
.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):
|
||||
return self.find(QTabWidget, "tabWidget")\
|
||||
.currentWidget()\
|
||||
@@ -303,6 +313,7 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
win = EditLateralContributionWindow(
|
||||
data=data,
|
||||
study=self._study,
|
||||
tab=tab,
|
||||
parent=self
|
||||
)
|
||||
win.show()
|
||||
|
||||
@@ -30,7 +30,7 @@ from platformdirs import user_cache_dir
|
||||
|
||||
from Solver.AdisTS import AdisTS
|
||||
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 PyQt5 import QtGui
|
||||
@@ -2082,7 +2082,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
|
||||
dialog.setDefaultSuffix(".BIN")
|
||||
# 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._last_solver is None:
|
||||
@@ -2102,7 +2102,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
if ".BIN" in file_name[0]:
|
||||
solver = Mage8("Mage8")
|
||||
else:
|
||||
solver = Rubar3("Rubar3")
|
||||
solver = RubarBE("RubarBE")
|
||||
|
||||
self.open_solver_results(
|
||||
solver,
|
||||
|
||||
@@ -192,11 +192,37 @@ class RunSolverTranslate(PamhyrTranslate):
|
||||
"Lateral contribution {name} on reach {reach} has no value: it "
|
||||
"is ignored."
|
||||
)
|
||||
self._dict["lateral_solid_unsupported"] = _translate(
|
||||
self._dict["lateral_solid_no_transport"] = _translate(
|
||||
"Solver",
|
||||
"Export cancelled: solid or suspension lateral contribution "
|
||||
"{name} on reach {reach}: RubarBE only reads the sediment of a "
|
||||
"liquid inflow, which Pamhyr2 cannot link to it. Remove it."
|
||||
"Solid lateral contribution {name} on reach {reach} is not taken "
|
||||
"into account: sediment transport (rubarbe_trased) is disabled "
|
||||
"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(
|
||||
"Solver",
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</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">
|
||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||
</widget>
|
||||
|
||||
@@ -59,16 +59,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</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 class="QWidget" name="verticalLayoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||
|
||||
+1
-17
@@ -428,15 +428,7 @@ class Config(SQL):
|
||||
new._cmd_solver = f"\"{path}{ext}\" @args @input"
|
||||
self._solvers.append(new)
|
||||
|
||||
# Rubar3
|
||||
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)
|
||||
# RubarBE
|
||||
ctor = solver_type_list["rubarbe"]
|
||||
new = ctor("default-RubarBE")
|
||||
new._description = "Default Pamhyr2 RubarBE version"
|
||||
@@ -444,14 +436,6 @@ class Config(SQL):
|
||||
new._cmd_solver = f""""{path}" @args @input"""
|
||||
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
|
||||
self.backup_enable = True
|
||||
# self.backup_path = ""
|
||||
|
||||
Reference in new issue
Block a user