RubarBE: export weirs and rectangular and circular orifices (ouvrag), and ignore other structure types with a warning

This commit is contained in:
Dylan Jeannin committed 2026-10-20 17:41:32 +02:00
1 parent 357b39758c
commit 5f64362f65
2 files changed
+186

No files matched your search

+128
View File
@@ -175,6 +175,8 @@ class Rubar3(CommandLineSolver):
saves = self._save_data(values, hydraulics)
laterals = self._lateral_data(
study.river, hydraulics, values, hydraulics["warnings"])
structures = self._structure_data(
study.river, hydraulics, hydraulics["warnings"])
except RubarExportError as error:
self._cancel(qlog, str(error))
return False
@@ -194,6 +196,7 @@ class Rubar3(CommandLineSolver):
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_stricklers(hydraulics, repertory, qlog, name=name)
self._export_boundaries(hydraulics, repertory, qlog, name=name)
@@ -630,6 +633,114 @@ class Rubar3(CommandLineSolver):
return [block[:3] for block in blocks]
##############
# 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 #
#########
@@ -1149,6 +1260,23 @@ class Rubar3(CommandLineSolver):
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"):
"""FROT (minor bed) and FROT2 (medium bed): LM, then one Strickler
per cell (notice, annex 1)"""
+58
View File
@@ -129,6 +129,64 @@ class RunSolverTranslate(PamhyrTranslate):
"Export cancelled: several {kind} boundary conditions at node "
"{node}."
)
self._dict["structure_section"] = _translate(
"Solver",
"Export cancelled: the section of hydraulic structure {name} is "
"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 "