mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e5153d63d | ||
|
|
797de09683 | ||
|
|
ef10588880 | ||
|
|
c2a8e17bf3 | ||
|
|
03979a8bea | ||
|
|
a4eee962f7 | ||
|
|
d8d1b4cd1f | ||
|
|
cdeca8a2e8 | ||
|
|
4d9c4cb5d0 | ||
|
|
618477b384 | ||
|
|
70a26d0036 | ||
|
|
c3afc4dd58 | ||
|
|
ef72e41b14 | ||
|
|
be7b5b892a | ||
|
|
5f64362f65 | ||
|
|
357b39758c | ||
|
|
a2f215984b | ||
|
|
b1cf45497b |
No files matched your search
@@ -44,6 +44,7 @@ __old__
|
|||||||
mage8/
|
mage8/
|
||||||
adists/
|
adists/
|
||||||
rubarbe/
|
rubarbe/
|
||||||
|
rubar/
|
||||||
|
|
||||||
linux/
|
linux/
|
||||||
windows/
|
windows/
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
+405
-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,24 +165,22 @@ 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(
|
||||||
study.river, hydraulics["edge"], values, trased)
|
study.river, hydraulics["edge"], values, trased,
|
||||||
|
hydraulics["warnings"])
|
||||||
saves = self._save_data(values, hydraulics)
|
saves = self._save_data(values, hydraulics)
|
||||||
|
laterals = self._lateral_data(
|
||||||
|
study.river, hydraulics, values, trased,
|
||||||
|
hydraulics["warnings"])
|
||||||
|
structures = self._structure_data(
|
||||||
|
study.river, hydraulics, hydraulics["warnings"])
|
||||||
except RubarExportError as error:
|
except RubarExportError as error:
|
||||||
self._cancel(qlog, str(error))
|
self._cancel(qlog, str(error))
|
||||||
return False
|
return False
|
||||||
@@ -189,11 +198,15 @@ class Rubar3(CommandLineSolver):
|
|||||||
self._export_geomac_i(geometry, repertory, qlog, name=name)
|
self._export_geomac_i(geometry, repertory, qlog, name=name)
|
||||||
self._export_mail(study, repertory, qlog, name=name)
|
self._export_mail(study, repertory, qlog, name=name)
|
||||||
self._export_devers(study, repertory, qlog, name=name)
|
self._export_devers(study, repertory, qlog, name=name)
|
||||||
|
if laterals is not None:
|
||||||
|
self._export_hydevs(laterals, trased, repertory, qlog, name=name)
|
||||||
|
self._export_ouvrag(structures, repertory, qlog, name=name)
|
||||||
self._export_condin(hydraulics, repertory, qlog, name=name)
|
self._export_condin(hydraulics, repertory, qlog, name=name)
|
||||||
self._export_stricklers(hydraulics, repertory, qlog, name=name)
|
self._export_stricklers(hydraulics, repertory, qlog, name=name)
|
||||||
self._export_boundaries(hydraulics, repertory, qlog, name=name)
|
self._export_boundaries(hydraulics, repertory, qlog, name=name)
|
||||||
if solid is not None:
|
if solid is not None:
|
||||||
self._export_hydros(solid, repertory, qlog, name=name)
|
self._export_hydros(solid, hydraulics["tinit"], repertory, qlog,
|
||||||
|
name=name)
|
||||||
if trased:
|
if trased:
|
||||||
self._export_condins(geometry, hydraulics["tinit"], repertory,
|
self._export_condins(geometry, hydraulics["tinit"], repertory,
|
||||||
qlog, name=name)
|
qlog, name=name)
|
||||||
@@ -222,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(
|
||||||
@@ -242,9 +262,17 @@ 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)
|
boundaries = self._check_boundaries(river, edge, values, warnings)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"edge": edge,
|
"edge": edge,
|
||||||
@@ -381,7 +409,7 @@ class Rubar3(CommandLineSolver):
|
|||||||
return float(old_pamhyr_date_to_timestamp(value))
|
return float(old_pamhyr_date_to_timestamp(value))
|
||||||
return float(value)
|
return float(value)
|
||||||
|
|
||||||
def _check_boundaries(self, river, edge, values):
|
def _check_boundaries(self, river, edge, values, warnings):
|
||||||
"""Liquid boundary conditions used for CONDAM, REGIME and CONDAV.
|
"""Liquid boundary conditions used for CONDAM, REGIME and CONDAV.
|
||||||
Return {'hydro': bc, 'condav': bc} (or None)"""
|
Return {'hydro': bc, 'condav': bc} (or None)"""
|
||||||
trad = RunSolverTranslate()
|
trad = RunSolverTranslate()
|
||||||
@@ -417,12 +445,12 @@ class Rubar3(CommandLineSolver):
|
|||||||
|
|
||||||
upstream = self._select_boundaries(
|
upstream = self._select_boundaries(
|
||||||
at(edge.node1), self._upstream_types[condam], edge.node1,
|
at(edge.node1), self._upstream_types[condam], edge.node1,
|
||||||
{"TD": f"CONDAM={condam}"}, f"CONDAM={condam}"
|
{"TD": f"CONDAM={condam}"}, f"CONDAM={condam}", warnings
|
||||||
)
|
)
|
||||||
downstream = self._select_boundaries(
|
downstream = self._select_boundaries(
|
||||||
at(edge.node2), self._downstream_types[condav], edge.node2,
|
at(edge.node2), self._downstream_types[condav], edge.node2,
|
||||||
{"ZD": f"CONDAV={condav}", "TZ": f"CONDAV={condav}"},
|
{"ZD": f"CONDAV={condav}", "TZ": f"CONDAV={condav}"},
|
||||||
f"CONDAV={condav}"
|
f"CONDAV={condav}", warnings
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -430,9 +458,27 @@ class Rubar3(CommandLineSolver):
|
|||||||
"condav": downstream.get("ZD", downstream.get("TZ")),
|
"condav": downstream.get("ZD", downstream.get("TZ")),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _select_boundaries(self, bcs, expected, node, required_by, setting):
|
@staticmethod
|
||||||
|
def _ignore_empty(bcs, node, warnings, meaningful=()):
|
||||||
|
"""A boundary condition without value carries no data: it is
|
||||||
|
ignored with a warning, except for the types where an empty
|
||||||
|
condition has a meaning. Return (kept, ignored)"""
|
||||||
|
trad = RunSolverTranslate()
|
||||||
|
kept, ignored = [], []
|
||||||
|
for bc in bcs:
|
||||||
|
if len(bc) == 0 and bc.bctype not in meaningful:
|
||||||
|
warnings.append(trad["boundary_empty_ignored"].format(
|
||||||
|
name=bc.name, node=node.name))
|
||||||
|
ignored.append(bc)
|
||||||
|
else:
|
||||||
|
kept.append(bc)
|
||||||
|
return kept, ignored
|
||||||
|
|
||||||
|
def _select_boundaries(self, bcs, expected, node, required_by, setting,
|
||||||
|
warnings, meaningful=()):
|
||||||
trad = RunSolverTranslate()
|
trad = RunSolverTranslate()
|
||||||
selected = {}
|
selected = {}
|
||||||
|
bcs, ignored = self._ignore_empty(bcs, node, warnings, meaningful)
|
||||||
|
|
||||||
for bc in bcs:
|
for bc in bcs:
|
||||||
kind = self._boundary_kinds[bc.bctype]
|
kind = self._boundary_kinds[bc.bctype]
|
||||||
@@ -443,9 +489,6 @@ class Rubar3(CommandLineSolver):
|
|||||||
if bc.bctype in selected:
|
if bc.bctype in selected:
|
||||||
raise RubarExportError(trad["boundary_ambiguous"].format(
|
raise RubarExportError(trad["boundary_ambiguous"].format(
|
||||||
kind=kind, node=node.name))
|
kind=kind, node=node.name))
|
||||||
if len(bc) == 0:
|
|
||||||
raise RubarExportError(trad["boundary_empty"].format(
|
|
||||||
name=bc.name, node=node.name))
|
|
||||||
times = [d[0] for d in bc.data]
|
times = [d[0] for d in bc.data]
|
||||||
if bc.bctype != "ZD" and any(
|
if bc.bctype != "ZD" and any(
|
||||||
b < a for a, b in zip(times, times[1:])):
|
b < a for a, b in zip(times, times[1:])):
|
||||||
@@ -455,18 +498,26 @@ class Rubar3(CommandLineSolver):
|
|||||||
|
|
||||||
for bctype in expected:
|
for bctype in expected:
|
||||||
if bctype not in selected:
|
if bctype not in selected:
|
||||||
raise RubarExportError(trad["boundary_missing"].format(
|
message = trad["boundary_missing"].format(
|
||||||
setting=required_by[bctype],
|
setting=required_by[bctype],
|
||||||
kind=self._boundary_kinds[bctype], node=node.name))
|
kind=self._boundary_kinds[bctype], node=node.name)
|
||||||
|
# The required condition may be the ignored empty one
|
||||||
|
empty = [bc.name for bc in ignored if bc.bctype == bctype]
|
||||||
|
if empty:
|
||||||
|
message += " " + trad["boundary_missing_empty"].format(
|
||||||
|
names=", ".join(empty))
|
||||||
|
raise RubarExportError(message)
|
||||||
|
|
||||||
return selected
|
return selected
|
||||||
|
|
||||||
def _solid_boundary(self, river, edge, values, trased):
|
def _solid_boundary(self, river, edge, values, trased, warnings):
|
||||||
"""Solid boundary condition written in HYDROS. RubarBE reads it
|
"""Solid boundary condition written in HYDROS. RubarBE reads it
|
||||||
at the upstream node when TRASED is enabled and CONDAM=1 (CONDAM=2
|
at the upstream node when TRASED is enabled and CONDAM=1 (CONDAM=2
|
||||||
is refused), and has no downstream solid condition (rubar_init.f90,
|
is refused), and has no downstream solid condition (rubar_init.f90,
|
||||||
inits). Without sediment transport the solid conditions are
|
inits). Without sediment transport the solid conditions are
|
||||||
ignored. Return the condition or None"""
|
ignored. At the upstream node, an empty solid condition means a
|
||||||
|
supply at equilibrium in Pamhyr2 (Mage 'default'), written as a
|
||||||
|
negative Qs (_export_hydros). Return the condition or None"""
|
||||||
if not trased:
|
if not trased:
|
||||||
return None
|
return None
|
||||||
trad = RunSolverTranslate()
|
trad = RunSolverTranslate()
|
||||||
@@ -476,17 +527,19 @@ class Rubar3(CommandLineSolver):
|
|||||||
bc for bc in river.boundary_condition.get_tab("solid")
|
bc for bc in river.boundary_condition.get_tab("solid")
|
||||||
if not bc.is_deleted() and bc.bctype == "SL"
|
if not bc.is_deleted() and bc.bctype == "SL"
|
||||||
]
|
]
|
||||||
for bc in solid:
|
downstream, _ = self._ignore_empty(
|
||||||
if bc.node == edge.node2:
|
[bc for bc in solid if bc.node == edge.node2], edge.node2,
|
||||||
|
warnings)
|
||||||
|
if downstream:
|
||||||
raise RubarExportError(
|
raise RubarExportError(
|
||||||
trad["solid_boundary_downstream"].format(
|
trad["solid_boundary_downstream"].format(
|
||||||
name=bc.name, node=edge.node2.name))
|
name=downstream[0].name, node=edge.node2.name))
|
||||||
|
|
||||||
expected = ("SL",) if condam == "1" else ()
|
expected = ("SL",) if condam == "1" else ()
|
||||||
selected = self._select_boundaries(
|
selected = self._select_boundaries(
|
||||||
[bc for bc in solid if bc.node == edge.node1], expected,
|
[bc for bc in solid if bc.node == edge.node1], expected,
|
||||||
edge.node1, {"SL": f"TRASED=O, CONDAM={condam}"},
|
edge.node1, {"SL": f"TRASED=O, CONDAM={condam}"},
|
||||||
f"CONDAM={condam}"
|
f"CONDAM={condam}", warnings, meaningful=("SL",)
|
||||||
)
|
)
|
||||||
bc = selected.get("SL")
|
bc = selected.get("SL")
|
||||||
if bc is None:
|
if bc is None:
|
||||||
@@ -502,6 +555,272 @@ class Rubar3(CommandLineSolver):
|
|||||||
name=bc.name, node=edge.node1.name))
|
name=bc.name, node=edge.node1.name))
|
||||||
return bc
|
return bc
|
||||||
|
|
||||||
|
############
|
||||||
|
# Laterals #
|
||||||
|
############
|
||||||
|
|
||||||
|
# Pamhyr2 lateral contribution types; evaporation is a withdrawal
|
||||||
|
_lateral_signs = {"LC": 1.0, "RA": 1.0, "EV": -1.0}
|
||||||
|
# QMU, TMU (variables.f90, LMAX*20): RubarBE copies the series of an
|
||||||
|
# inflow in each cell of its zone, without checking this size
|
||||||
|
_max_lateral_points = 3000 * 20
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
A contribution from section b to section e (1-based) covers the
|
||||||
|
cells b+1 to e, i.e. from section b to section e (RubarBE divides
|
||||||
|
the discharge by this length); a contribution at a single section
|
||||||
|
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.
|
||||||
|
|
||||||
|
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"]
|
||||||
|
iodev = values.get("rubarbe_iodev", "n").lower() in ("y", "o")
|
||||||
|
lst = river.lateral_contribution
|
||||||
|
|
||||||
|
def on_reach(tab):
|
||||||
|
# A contribution without value carries no data: ignored
|
||||||
|
kept = []
|
||||||
|
for lc in lst.get_tab(tab):
|
||||||
|
if lc.is_deleted() or lc.reach != edge:
|
||||||
|
continue
|
||||||
|
if len(lc) == 0:
|
||||||
|
warnings.append(trad["lateral_empty_ignored"].format(
|
||||||
|
name=lc.name, reach=edge.name))
|
||||||
|
else:
|
||||||
|
kept.append(lc)
|
||||||
|
return kept
|
||||||
|
|
||||||
|
lcs = on_reach("liquid")
|
||||||
|
solids = on_reach("solid")
|
||||||
|
|
||||||
|
# Not read by RubarBE: the user is warned, the computation runs
|
||||||
|
# without them
|
||||||
|
if not iodev:
|
||||||
|
for lc in lcs + solids:
|
||||||
|
warnings.append(trad["lateral_iodev_disabled"].format(
|
||||||
|
name=lc.name, reach=edge.name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)]))
|
||||||
|
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))
|
||||||
|
|
||||||
|
blocks.sort(key=lambda block: block[0])
|
||||||
|
for (_, last, _, name0), (first, _, _, name1) in zip(
|
||||||
|
blocks, blocks[1:]):
|
||||||
|
if first <= last:
|
||||||
|
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 result)
|
||||||
|
if points > self._max_lateral_points:
|
||||||
|
raise RubarExportError(trad["lateral_too_long"].format(
|
||||||
|
count=points, maximum=self._max_lateral_points))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
##############
|
||||||
|
# Structures #
|
||||||
|
##############
|
||||||
|
|
||||||
|
# Pamhyr2 basic structures (those of Mage) with a RubarBE equivalent
|
||||||
|
# (notice, annex 5): weir and rectangular orifice as a weir-orifice D,
|
||||||
|
# circular orifice as O
|
||||||
|
_structure_types = {"S1": "D", "OR": "D", "OC": "O"}
|
||||||
|
# Loading elevation of a weir that never loads (default of Pamhyr2)
|
||||||
|
_no_loading = 9999.999
|
||||||
|
# nou1Dmax, noe1Dmax (variables.f90)
|
||||||
|
_max_structures = 50
|
||||||
|
_max_elementary = 10
|
||||||
|
|
||||||
|
def _structure_data(self, river, hydraulics, warnings):
|
||||||
|
"""Structures of OUVRAG (rubar_init.f90, LOUVR1D). A Pamhyr2
|
||||||
|
structure sits on a section: RubarBE structure on this single
|
||||||
|
interface, with reference -2 (the discharge is that of the
|
||||||
|
structure only), as for Mage. Disabled structures are skipped, as
|
||||||
|
for Mage. Return [(rk, [(type, L, zd, zm, coefficient), ...]), ...]
|
||||||
|
sorted from upstream. Raise RubarExportError with a user message"""
|
||||||
|
trad = RunSolverTranslate()
|
||||||
|
edge = hydraulics["edge"]
|
||||||
|
sections = {id(p) for p in hydraulics["profiles"]}
|
||||||
|
|
||||||
|
structures = {}
|
||||||
|
for hs in river.hydraulic_structures.lst:
|
||||||
|
if (hs.is_deleted() or not hs.enabled
|
||||||
|
or hs.input_reach is None or hs.input_section is None
|
||||||
|
or hs.input_reach != edge):
|
||||||
|
continue
|
||||||
|
if id(hs.input_section) not in sections:
|
||||||
|
raise RubarExportError(trad["structure_section"].format(
|
||||||
|
name=hs.name, reach=edge.name))
|
||||||
|
|
||||||
|
# Basic structures without RubarBE equivalent are not taken
|
||||||
|
# into account: the user is warned
|
||||||
|
lines = []
|
||||||
|
for bhs in hs.basic_structures:
|
||||||
|
if not bhs.enabled or bhs.is_deleted():
|
||||||
|
continue
|
||||||
|
if bhs.type not in self._structure_types:
|
||||||
|
warnings.append(trad["structure_type_ignored"].format(
|
||||||
|
name=self._basic_label(hs, bhs), type=bhs.type))
|
||||||
|
continue
|
||||||
|
lines.append(self._elementary_structure(hs, bhs))
|
||||||
|
if not lines:
|
||||||
|
warnings.append(trad["structure_empty_ignored"].format(
|
||||||
|
name=hs.name))
|
||||||
|
continue
|
||||||
|
if len(lines) > self._max_elementary:
|
||||||
|
raise RubarExportError(trad["structure_too_many"].format(
|
||||||
|
name=hs.name, count=len(lines),
|
||||||
|
maximum=self._max_elementary))
|
||||||
|
|
||||||
|
rk = self._section_rk(hs.input_section)
|
||||||
|
if rk in structures:
|
||||||
|
raise RubarExportError(trad["structure_same_section"].format(
|
||||||
|
first=structures[rk][1], second=hs.name, rk=f"{rk:g}"))
|
||||||
|
structures[rk] = (lines, hs.name)
|
||||||
|
|
||||||
|
if len(structures) > self._max_structures:
|
||||||
|
raise RubarExportError(trad["structure_too_many_structures"]
|
||||||
|
.format(count=len(structures),
|
||||||
|
maximum=self._max_structures))
|
||||||
|
return [(rk, structures[rk][0]) for rk in sorted(structures)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _basic_label(hs, bhs):
|
||||||
|
return f"{bhs.name or bhs.type} ({hs.name})"
|
||||||
|
|
||||||
|
def _elementary_structure(self, hs, bhs):
|
||||||
|
"""RubarBE line of a basic structure of a type of _structure_types:
|
||||||
|
(type, L, zd, zm, coefficient) for D (zm: loading elevation),
|
||||||
|
(type, L, zd, D, coefficient) for O (L: pipe length, none in
|
||||||
|
Pamhyr2: 0, a thin wall orifice)"""
|
||||||
|
trad = RunSolverTranslate()
|
||||||
|
label = self._basic_label(hs, bhs)
|
||||||
|
values = [float(v.value) for v in bhs.parameters]
|
||||||
|
if bhs.type == "S1":
|
||||||
|
width, elevation, coefficient = values
|
||||||
|
line = ("D", width, elevation, self._no_loading, coefficient)
|
||||||
|
elif bhs.type == "OR":
|
||||||
|
width, elevation, loading, coefficient, maximal = values
|
||||||
|
if maximal < self._no_loading:
|
||||||
|
raise RubarExportError(trad["structure_maximal_loading"]
|
||||||
|
.format(name=label))
|
||||||
|
line = ("D", width, elevation, loading, coefficient)
|
||||||
|
else:
|
||||||
|
diameter, elevation, siltation, coefficient = values
|
||||||
|
if siltation != 0:
|
||||||
|
raise RubarExportError(trad["structure_siltation"].format(
|
||||||
|
name=label))
|
||||||
|
line = ("O", 0.0, elevation, diameter, coefficient)
|
||||||
|
|
||||||
|
kind, size, zd, zm, coefficient = line
|
||||||
|
if (size <= 0 if kind == "D" else zm <= 0) or coefficient <= 0:
|
||||||
|
raise RubarExportError(trad["structure_invalid"].format(
|
||||||
|
name=label))
|
||||||
|
if kind == "D" and zm <= zd:
|
||||||
|
raise RubarExportError(trad["structure_loading_below"].format(
|
||||||
|
name=label))
|
||||||
|
# Fixed columns (A1, A9, 4F10.0)
|
||||||
|
if any(len(f"{v:10.3f}") > 10 for v in line[1:]):
|
||||||
|
raise RubarExportError(trad["structure_format"].format(
|
||||||
|
name=label))
|
||||||
|
return line
|
||||||
|
|
||||||
#########
|
#########
|
||||||
# Saves #
|
# Saves #
|
||||||
#########
|
#########
|
||||||
@@ -978,6 +1297,9 @@ class Rubar3(CommandLineSolver):
|
|||||||
self._write_parameters(repertory, f"tnprof.{name}", lines)
|
self._write_parameters(repertory, f"tnprof.{name}", lines)
|
||||||
|
|
||||||
def _export_devers(self, study, repertory, qlog, name="0"):
|
def _export_devers(self, study, repertory, qlog, name="0"):
|
||||||
|
"""DEVERS, required by RubarBE when IODEV=O: per cell, weir
|
||||||
|
level, coefficient and exponent (notice annex 1). Pamhyr2 has no
|
||||||
|
lateral weir data: a zero coefficient everywhere (no overflow)"""
|
||||||
if qlog is not None:
|
if qlog is not None:
|
||||||
qlog.put("Export DEVERS file")
|
qlog.put("Export DEVERS file")
|
||||||
|
|
||||||
@@ -999,6 +1321,44 @@ class Rubar3(CommandLineSolver):
|
|||||||
|
|
||||||
reach_ind += 1
|
reach_ind += 1
|
||||||
|
|
||||||
|
def _export_hydevs(self, laterals, trased, repertory, qlog, name="0"):
|
||||||
|
"""HYDEVS: per inflow, first and last cells and number of points,
|
||||||
|
then (Q, t) per point (rubar_init.f90, init2). With sediment
|
||||||
|
transport, D and S on the first line and (Q, t, Qs, ts) per point:
|
||||||
|
Pamhyr2 gives no sediment for a liquid inflow, so Qs = 0 (and
|
||||||
|
D = S = 0, the sediment of the main channel). Written, possibly
|
||||||
|
empty, so that a previous HYDEVS of the directory is not used"""
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export HYDEVS file")
|
||||||
|
lines = []
|
||||||
|
for first, last, data, (d50, sigma), length in laterals:
|
||||||
|
if trased:
|
||||||
|
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]
|
||||||
|
self._write_parameters(repertory, f"hydevs.{name}", lines)
|
||||||
|
|
||||||
|
def _export_ouvrag(self, structures, repertory, qlog, name="0"):
|
||||||
|
"""OUVRAG (notice annex 1, rubar_init.f90, LOUVR1D): per
|
||||||
|
structure, upstream abscissa, reference and reach, downstream
|
||||||
|
abscissa, number of basic structures and reach, then one line per
|
||||||
|
basic structure in (A1, A9, 4F10.0). Written, possibly empty, so
|
||||||
|
that a previous OUVRAG of the directory is not used"""
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export OUVRAG file")
|
||||||
|
lines = []
|
||||||
|
for rk, elementary in structures:
|
||||||
|
lines.append(f"{rk:11.3f} {-2:4d} {1:4d}\n")
|
||||||
|
lines.append(f"{rk:11.3f} {len(elementary):4d} {1:4d}\n")
|
||||||
|
for kind, size, zd, zm, coefficient in elementary:
|
||||||
|
lines.append(f"{kind}{'':9}{size:10.3f}{zd:10.3f}"
|
||||||
|
f"{zm:10.3f}{coefficient:10.3f}\n")
|
||||||
|
self._write_parameters(repertory, f"ouvrag.{name}", lines)
|
||||||
|
|
||||||
def _export_stricklers(self, hydraulics, repertory, qlog, name="0"):
|
def _export_stricklers(self, hydraulics, repertory, qlog, name="0"):
|
||||||
"""FROT (minor bed) and FROT2 (medium bed): LM, then one Strickler
|
"""FROT (minor bed) and FROT2 (medium bed): LM, then one Strickler
|
||||||
per cell (notice, annex 1)"""
|
per cell (notice, annex 1)"""
|
||||||
@@ -1060,16 +1420,21 @@ class Rubar3(CommandLineSolver):
|
|||||||
lines = [header(len(bc))] + records(bc, columns)
|
lines = [header(len(bc))] + records(bc, columns)
|
||||||
self._write_parameters(repertory, f"{filename}.{name}", lines)
|
self._write_parameters(repertory, f"{filename}.{name}", lines)
|
||||||
|
|
||||||
def _export_hydros(self, bc, repertory, qlog, name="0"):
|
def _export_hydros(self, bc, tinit, repertory, qlog, name="0"):
|
||||||
"""HYDROS: number of points, then Qs, t, D, S (notice annex 1).
|
"""HYDROS: number of points, then Qs, t, D, S (notice annex 1).
|
||||||
Qs is a solid discharge (kg/s) or a concentration (kg/m3)
|
Qs is a solid discharge (kg/s) or a concentration (kg/m3)
|
||||||
according to UNISOL, D and S are those of the condition"""
|
according to UNISOL, D and S are those of the condition. An
|
||||||
|
empty condition (supply at equilibrium) is written as a single
|
||||||
|
negative Qs: RubarBE then takes the transport capacity of the
|
||||||
|
upstream section at each time step (notice §3.6; rubarbe.f90,
|
||||||
|
'si Hydros.etude contient un Qs<0')"""
|
||||||
if qlog is not None:
|
if qlog is not None:
|
||||||
qlog.put("Export HYDROS file")
|
qlog.put("Export HYDROS file")
|
||||||
d50, sigma = float(bc.d50), float(bc.sigma)
|
d50, sigma = float(bc.d50), float(bc.sigma)
|
||||||
lines = [f"{len(bc)}\n"] + [
|
data = [(t, qs) for t, qs in bc.data] or [(tinit, -1.0)]
|
||||||
|
lines = [f"{len(data)}\n"] + [
|
||||||
f"{float(qs)!r} {float(t)!r} {d50!r} {sigma!r}\n"
|
f"{float(qs)!r} {float(t)!r} {d50!r} {sigma!r}\n"
|
||||||
for t, qs in bc.data
|
for t, qs in data
|
||||||
]
|
]
|
||||||
self._write_parameters(repertory, f"hydros.{name}", lines)
|
self._write_parameters(repertory, f"hydros.{name}", lines)
|
||||||
|
|
||||||
|
|||||||
@@ -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,8 +105,18 @@ class ComboBoxDelegate(QItemDelegate):
|
|||||||
strickler
|
strickler
|
||||||
)
|
)
|
||||||
elif self._mode == "rk":
|
elif self._mode == "rk":
|
||||||
|
if self._data is None:
|
||||||
self.editor.addItems(
|
self.editor.addItems(
|
||||||
list(map(str, self._data.reach.get_rk()))
|
["-"]
|
||||||
|
)
|
||||||
|
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)))
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -129,10 +129,143 @@ class RunSolverTranslate(PamhyrTranslate):
|
|||||||
"Export cancelled: several {kind} boundary conditions at node "
|
"Export cancelled: several {kind} boundary conditions at node "
|
||||||
"{node}."
|
"{node}."
|
||||||
)
|
)
|
||||||
self._dict["boundary_empty"] = _translate(
|
self._dict["structure_section"] = _translate(
|
||||||
"Solver",
|
"Solver",
|
||||||
"Export cancelled: boundary condition {name} at node {node} "
|
"Export cancelled: the section of hydraulic structure {name} is "
|
||||||
"has no value."
|
"not a profile of reach {reach}."
|
||||||
|
)
|
||||||
|
self._dict["structure_empty_ignored"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Hydraulic structure {name} has no enabled basic structure "
|
||||||
|
"that RubarBE takes into account: it is ignored."
|
||||||
|
)
|
||||||
|
self._dict["structure_too_many"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: hydraulic structure {name} has {count} "
|
||||||
|
"enabled basic structures, RubarBE accepts at most {maximum}."
|
||||||
|
)
|
||||||
|
self._dict["structure_too_many_structures"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: {count} hydraulic structures, RubarBE accepts "
|
||||||
|
"at most {maximum}."
|
||||||
|
)
|
||||||
|
self._dict["structure_same_section"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: hydraulic structures {first} and {second} are "
|
||||||
|
"on the same section (rk {rk}). Put their basic structures in a "
|
||||||
|
"single hydraulic structure."
|
||||||
|
)
|
||||||
|
self._dict["structure_type_ignored"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Basic structure {name} of type {type} has no RubarBE "
|
||||||
|
"equivalent: it is not taken into account. RubarBE accepts "
|
||||||
|
"discharge weirs (S1), rectangular orifices (OR) and circular "
|
||||||
|
"orifices (OC)."
|
||||||
|
)
|
||||||
|
self._dict["structure_maximal_loading"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: rectangular orifice {name}: RubarBE has no "
|
||||||
|
"maximal loading elevation. Leave it at 9999.999."
|
||||||
|
)
|
||||||
|
self._dict["structure_siltation"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: circular orifice {name}: RubarBE has no "
|
||||||
|
"siltation height. Set it to 0."
|
||||||
|
)
|
||||||
|
self._dict["structure_invalid"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: basic structure {name} must have a width (or "
|
||||||
|
"diameter) > 0 and a discharge coefficient > 0."
|
||||||
|
)
|
||||||
|
self._dict["structure_loading_below"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: basic structure {name}: the loading elevation "
|
||||||
|
"must be above the weir elevation."
|
||||||
|
)
|
||||||
|
self._dict["structure_format"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: a value of basic structure {name} does not fit "
|
||||||
|
"the 10 columns of RubarBE (3 decimals)."
|
||||||
|
)
|
||||||
|
self._dict["lateral_empty_ignored"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Lateral contribution {name} on reach {reach} has no value: it "
|
||||||
|
"is ignored."
|
||||||
|
)
|
||||||
|
self._dict["lateral_solid_no_transport"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"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",
|
||||||
|
"Lateral contribution {name} on reach {reach} is not taken into "
|
||||||
|
"account: lateral overflows (rubarbe_iodev) are disabled in the "
|
||||||
|
"solver parameters."
|
||||||
|
)
|
||||||
|
self._dict["lateral_type_unsupported"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: lateral contribution {name} on reach {reach} "
|
||||||
|
"has no type. Choose a type (lateral sources, rain or "
|
||||||
|
"evaporation)."
|
||||||
|
)
|
||||||
|
self._dict["lateral_sections"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: the sections of lateral contribution {name} "
|
||||||
|
"are not profiles of reach {reach}."
|
||||||
|
)
|
||||||
|
self._dict["lateral_not_sorted"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: the times of lateral contribution {name} on "
|
||||||
|
"reach {reach} must be increasing."
|
||||||
|
)
|
||||||
|
self._dict["lateral_overlap"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: lateral contributions {first} and {second} "
|
||||||
|
"on reach {reach} overlap, which RubarBE does not accept. Merge "
|
||||||
|
"them or move one of them."
|
||||||
|
)
|
||||||
|
self._dict["lateral_too_long"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Export cancelled: the lateral contributions give {count} values "
|
||||||
|
"(number of points times number of cells of their zone), "
|
||||||
|
"RubarBE accepts at most {maximum}."
|
||||||
|
)
|
||||||
|
self._dict["boundary_empty_ignored"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Boundary condition {name} at node {node} has no value: it is "
|
||||||
|
"ignored."
|
||||||
|
)
|
||||||
|
self._dict["boundary_missing_empty"] = _translate(
|
||||||
|
"Solver",
|
||||||
|
"Empty boundary condition(s) ignored: {names}."
|
||||||
)
|
)
|
||||||
self._dict["boundary_not_sorted"] = _translate(
|
self._dict["boundary_not_sorted"] = _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="QWidget" name="verticalLayoutWidget_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_table">
|
||||||
|
<item>
|
||||||
<widget class="QTableView" name="tableView"/>
|
<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