Compare commits

...
4 Commits
6 changed files with 430 additions and 68 deletions

No files matched your search

+4 -1
View File
@@ -104,7 +104,10 @@ class Profile(SQLSubModel):
return any(map(lambda ts: "sl" in self._data[ts], self._data)) return any(map(lambda ts: "sl" in self._data[ts], self._data))
def has_bedload(self): def has_bedload(self):
return any(map(lambda ts: "sl" in self._data[ts], self._data)) # Sediment layers (Mage) or bed level of the section (RubarBE)
return any(map(lambda ts: ("sl" in self._data[ts] or
"zfd" in self._data[ts]),
self._data))
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
+316 -62
View File
@@ -17,6 +17,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import re
import logging import logging
import math import math
from decimal import Decimal, InvalidOperation, localcontext from decimal import Decimal, InvalidOperation, localcontext
@@ -76,11 +77,14 @@ class Rubar3(CommandLineSolver):
("rubarbe_fdeb2", "10"), ("rubarbe_fdeb2", "10"),
("rubarbe_fdeb3", "100"), ("rubarbe_fdeb3", "100"),
("rubarbe_tf_1", "y"), ("rubarbe_tf_1", "y"),
("rubarbe_tf_2", "y"), # TF(2) and TF(4) need the abscissas and times below
("rubarbe_tf_2", "n"),
("rubarbe_tf_3", "y"), ("rubarbe_tf_3", "y"),
("rubarbe_tf_4", "y"), ("rubarbe_tf_4", "n"),
("rubarbe_tf_5", "y"), ("rubarbe_tf_5", "y"),
("rubarbe_tf_6", "n"), ("rubarbe_tf_6", "n"),
("rubarbe_abshyd", ""),
("rubarbe_tnprof", ""),
("rubarbe_trased", "n"), ("rubarbe_trased", "n"),
("rubarbe_optfpc", "0"), ("rubarbe_optfpc", "0"),
# trased parameters # trased parameters
@@ -160,23 +164,14 @@ class Rubar3(CommandLineSolver):
return False return False
trased = self._parameter_flag(study, "trased") == "O" trased = self._parameter_flag(study, "trased") == "O"
if trased:
# Notice §3.6: HYDROS is required at each upstream boundary
missing = self._reaches_without_solid_boundary(study)
if missing:
trad = RunSolverTranslate()
message = trad["missing_solid_boundary"].format(
reaches=", ".join(missing)
)
logger.warning(message)
if qlog is not None:
qlog.put(trad["warning"].format(message=message))
return False
# Check the hydraulic data before writing any file # Check the hydraulic data before writing any file
try: try:
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(
study.river, hydraulics["edge"], values, trased)
saves = self._save_data(values, hydraulics)
except RubarExportError as error: except RubarExportError as error:
self._cancel(qlog, str(error)) self._cancel(qlog, str(error))
return False return False
@@ -197,7 +192,12 @@ class Rubar3(CommandLineSolver):
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)
# self._export_abshyd(study, repertory, qlog, name=name) if solid is not None:
self._export_hydros(solid, repertory, qlog, name=name)
if trased:
self._export_condins(geometry, hydraulics["tinit"], repertory,
qlog, name=name)
self._export_saves(saves, repertory, qlog, name=name)
return True return True
@@ -217,7 +217,10 @@ class Rubar3(CommandLineSolver):
"5": (), "7": ()} "5": (), "7": ()}
_boundary_kinds = {"TD": "hydrograph Q(t)", "TZ": "water level Z(t)", _boundary_kinds = {"TD": "hydrograph Q(t)", "TZ": "water level Z(t)",
"ZD": "rating curve Q(Z)", "ZD": "rating curve Q(Z)",
"PC": "punctual contribution"} "PC": "punctual contribution",
"SL": "solid discharge Qs(t)"}
# NCLMAX (variables.f90): points of all the solid hydrographs
_max_solid_points = 1000
def _hydraulic_data(self, study, values): def _hydraulic_data(self, study, values):
"""Check the single reach data needed by DONNEE, MAIL, CONDIN, """Check the single reach data needed by DONNEE, MAIL, CONDIN,
@@ -307,17 +310,11 @@ class Rubar3(CommandLineSolver):
name=names[marker], profile=self._profile_label(profile), name=names[marker], profile=self._profile_label(profile),
reach=edge.name)) reach=edge.name))
# Defaults of RubarBE: first and last points for G and D, G and D left, right, active_left, active_right = self._bed_limits(markers)
# for g and d. Erosion only applies strictly between g and d
def index(marker, default):
return markers.index(marker) if marker in markers else default
left = index("G", 0)
right = index("D", len(points) - 1)
limits = [("main_channel", "rg", "rd", left, right)] limits = [("main_channel", "rg", "rd", left, right)]
if trased: if trased:
limits.append(("active_bed", "g", "d", limits.append(("active_bed", "g", "d",
index("g", left), index("d", right))) active_left, active_right))
for bed, lname, rname, i, j in limits: for bed, lname, rname, i, j in limits:
if j - i < 2: if j - i < 2:
raise RubarExportError(trad["bed_limits_invalid"].format( raise RubarExportError(trad["bed_limits_invalid"].format(
@@ -325,6 +322,18 @@ class Rubar3(CommandLineSolver):
profile=self._profile_label(profile), reach=edge.name)) profile=self._profile_label(profile), reach=edge.name))
return markers return markers
@staticmethod
def _bed_limits(markers):
"""Indices of the G, D, g and d points of a section. Defaults of
RubarBE: first and last points for G and D, G and D for g and d.
Erosion only applies strictly between g and d"""
def index(marker, default):
return markers.index(marker) if marker in markers else default
left = index("G", 0)
right = index("D", len(markers) - 1)
return left, right, index("g", left), index("d", right)
def _compartments(self, edge, profile, number, point): def _compartments(self, edge, profile, number, point):
"""Sediment layers of a point as RubarBE compartments, from the """Sediment layers of a point as RubarBE compartments, from the
top: (top elevation, D, S, critical stress). A non erodible top: (top elevation, D, S, critical stress). A non erodible
@@ -452,6 +461,140 @@ class Rubar3(CommandLineSolver):
return selected return selected
def _solid_boundary(self, river, edge, values, trased):
"""Solid boundary condition written in HYDROS. RubarBE reads it
at the upstream node when TRASED is enabled and CONDAM=1 (CONDAM=2
is refused), and has no downstream solid condition (rubar_init.f90,
inits). Without sediment transport the solid conditions are
ignored. Return the condition or None"""
if not trased:
return None
trad = RunSolverTranslate()
condam = values.get("rubarbe_condam", "")
solid = [
bc for bc in river.boundary_condition.get_tab("solid")
if not bc.is_deleted() and bc.bctype == "SL"
]
for bc in solid:
if bc.node == edge.node2:
raise RubarExportError(
trad["solid_boundary_downstream"].format(
name=bc.name, node=edge.node2.name))
expected = ("SL",) if condam == "1" else ()
selected = self._select_boundaries(
[bc for bc in solid if bc.node == edge.node1], expected,
edge.node1, {"SL": f"TRASED=O, CONDAM={condam}"},
f"CONDAM={condam}"
)
bc = selected.get("SL")
if bc is None:
return None
if len(bc) > self._max_solid_points:
raise RubarExportError(trad["solid_boundary_too_long"].format(
name=bc.name, node=edge.node1.name, count=len(bc),
maximum=self._max_solid_points))
# RubarBE stops on a zero D or S
if not (bc.d50 > 0 and bc.sigma >= 1):
raise RubarExportError(trad["solid_boundary_invalid"].format(
name=bc.name, node=edge.node1.name))
return bc
#########
# Saves #
#########
# NBHYPR (variables.f90): abscissas of ABSHYD and times of TNPROF.
# RubarBE does not check this size
_max_saves = 100
@staticmethod
def save_list(text, times=False):
"""Numbers of a list parameter (ABSHYD abscissas, TNPROF times),
separated by spaces or semicolons. Times are in seconds or in
days:hours:minutes:seconds. Raise ValueError with the invalid
token"""
values = []
for token in re.split(r"[\s;]+", text.strip()):
if token == "":
continue
try:
if times and ":" in token:
parts = token.split(":")
if len(parts) != 4 or not all(
p.isascii() and p.isdigit() for p in parts):
raise ValueError(token)
value = float(sum(
int(p) * factor for p, factor in
zip(parts, (86400, 3600, 60, 1))))
else:
value = float(token)
except ValueError:
raise ValueError(token) from None
if not math.isfinite(value):
raise ValueError(token)
values.append(value)
return values
def _save_data(self, values, hydraulics):
"""Abscissas of ABSHYD, read when TF(2)=O, and times of TNPROF,
always read: they give the saves of PROFIL and PROFILS when
TF(4)=O and of the complementary outputs when IOVIS=O
(rubar_init.f90, init2; rubarbe.f90, SAVEVISU). Return
{'abshyd': [rk] or None, 'tnprof': [t]}"""
trad = RunSolverTranslate()
lists = {}
for key, times in (("abshyd", False), ("tnprof", True)):
key = f"rubarbe_{key}"
try:
lists[key] = self.save_list(values.get(key, ""), times)
except ValueError as error:
raise RubarExportError(trad["save_list_invalid"].format(
key=key, value=str(error)))
if len(lists[key]) > self._max_saves:
raise RubarExportError(trad["save_list_too_long"].format(
key=key, count=len(lists[key]),
maximum=self._max_saves))
def enabled(flag):
return values.get(f"rubarbe_{flag}", "n").lower() in ("y", "o")
for flag, key in (("tf_2", "rubarbe_abshyd"),
("tf_4", "rubarbe_tnprof")):
if enabled(flag) and not lists[key]:
raise RubarExportError(trad["save_list_required"].format(
flag=f"rubarbe_{flag}", key=key))
# RubarBE looks for each abscissa between two centres of cells,
# from the first to the last section
abshyd = None
if enabled("tf_2"):
edge = hydraulics["edge"]
first = self._section_rk(hydraulics["profiles"][0])
last = self._section_rk(hydraulics["profiles"][-1])
for rk in lists["rubarbe_abshyd"]:
if not first <= round(rk, 3) <= last:
raise RubarExportError(trad["abshyd_outside"].format(
rk=f"{rk:g}", first=f"{first:g}", last=f"{last:g}",
reach=edge.name))
abshyd = lists["rubarbe_abshyd"]
# Times outside the computation do not falsify it: RubarBE saves
# the earlier ones at the first time steps and never reaches the
# later ones
tinit = hydraulics["tinit"]
tmax = self._seconds(values.get("rubarbe_tmax", "0"))
tnprof = sorted(lists["rubarbe_tnprof"])
outside = [t for t in tnprof if not tinit <= t <= tmax]
if outside:
hydraulics["warnings"].append(trad["tnprof_outside"].format(
times=" ".join(f"{t:g}" for t in outside),
tinit=f"{tinit:g}", tmax=f"{tmax:g}"))
return {"abshyd": abshyd, "tnprof": tnprof}
@staticmethod @staticmethod
def _interpolated_frictions(edge): def _interpolated_frictions(edge):
"""Minor and medium Strickler at each centre of cell. The model """Minor and medium Strickler at each centre of cell. The model
@@ -578,20 +721,6 @@ class Rubar3(CommandLineSolver):
return False return False
return True return True
@staticmethod
def _reaches_without_solid_boundary(study):
river = study.river
solid = [
bc for bc in river.boundary_condition.get_tab("solid")
if not bc.is_deleted() and bc.bctype == "SL" and len(bc) > 0
]
return [
edge.name for edge in river.enable_edges()
if river.is_upstream_node(edge.node1)
and not any(bc.node == edge.node1 for bc in solid)
]
def _parameter_values(self, study): def _parameter_values(self, study):
return {p.name: str(p.value).strip() return {p.name: str(p.value).strip()
for p in study.river.get_params(self.type).parameters} for p in study.river.get_params(self.type).parameters}
@@ -825,27 +954,28 @@ class Rubar3(CommandLineSolver):
self._write_parameters(repertory, f"mail.{name}", lines) self._write_parameters(repertory, f"mail.{name}", lines)
def _export_abshyd(self, study, repertory, qlog, name="0"): def _export_saves(self, saves, repertory, qlog, name="0"):
"""ABSHYD (number, then I4, 1X, F11.3 per abscissa; single reach,
its number is omitted) when TF(2)=O, and TNPROF (number, then
index and time per line). TNPROF is always read by RubarBE: an
empty list is written as 0, so that a previous TNPROF of the
directory is not used"""
if saves["abshyd"] is not None:
if qlog is not None: if qlog is not None:
qlog.put("Export ABSHYD file") qlog.put("Export ABSHYD file")
lines = [f"{len(saves['abshyd']):>4}\n"] + [
f"{i:>4} {rk:>11.3f}\n"
for i, rk in enumerate(saves["abshyd"], 1)
]
self._write_parameters(repertory, f"abshyd.{name}", lines)
with open( if qlog is not None:
os.path.join( qlog.put("Export TNPROF file")
repertory, f"abshyd.{name}" lines = [f"{len(saves['tnprof']):>12}\n"] + [
), "w+" f"{i:>12} {t:>24.3f}\n"
) as f: for i, t in enumerate(saves["tnprof"], 1)
reach_ind = 1 ]
for edge in study.river.enable_edges(): self._write_parameters(repertory, f"tnprof.{name}", lines)
reach = edge.reach
lm = len(reach) + 1
f.write(f"{lm:>13}\n")
ind = 1
for mail in reach.inter_profiles_rk():
f.write(f"{ind:>4} {mail:15.3f} {reach_ind:>4}\n")
ind += 1
reach_ind += 1
def _export_devers(self, study, repertory, qlog, name="0"): def _export_devers(self, study, repertory, qlog, name="0"):
if qlog is not None: if qlog is not None:
@@ -930,6 +1060,40 @@ 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"):
"""HYDROS: number of points, then Qs, t, D, S (notice annex 1).
Qs is a solid discharge (kg/s) or a concentration (kg/m3)
according to UNISOL, D and S are those of the condition"""
if qlog is not None:
qlog.put("Export HYDROS file")
d50, sigma = float(bc.d50), float(bc.sigma)
lines = [f"{len(bc)}\n"] + [
f"{float(qs)!r} {float(t)!r} {d50!r} {sigma!r}\n"
for t, qs in bc.data
]
self._write_parameters(repertory, f"hydros.{name}", lines)
def _export_condins(self, geometry, tinit, repertory, qlog, name="0"):
"""CONDINS: TINIT, then for each section (intermaille) its number,
Qs, D and S of the active layer (notice annex 1, rubar_init.f90,
inits). Pamhyr2 holds no initial solid condition: Qs = 0 (empty
active layer) and D, S are the mean of the top compartments of the
points strictly between g and d, as advised by the notice §3.5"""
if qlog is not None:
qlog.put("Export CONDINS file")
lines = [f"{tinit!r}\n"]
for i, (_, rows) in enumerate(geometry, 1):
markers = [marker for marker, _, _ in rows]
_, _, left, right = self._bed_limits(markers)
tops = [compartments[0] for _, _, compartments
in rows[left + 1:right]]
# Rounded to 10 significant digits, so that a mean of equal
# values is written as these values
d = float(f"{sum(c[1] for c in tops) / len(tops):.10g}")
s = float(f"{sum(c[2] for c in tops) / len(tops):.10g}")
lines.append(f"{i} 0.0 {d!r} {s!r}\n")
self._write_parameters(repertory, f"condins.{name}", lines)
def read_profil(self, study, fname, results, qlog=None, name="0"): def read_profil(self, study, fname, results, qlog=None, name="0"):
logger.info(f"read: profil.{name}") logger.info(f"read: profil.{name}")
with open(fname, "r") as f: with open(fname, "r") as f:
@@ -949,10 +1113,16 @@ class Rubar3(CommandLineSolver):
else: else:
return (0.0, 0.0, 0.0, 0.0) return (0.0, 0.0, 0.0, 0.0)
def set_and_compute_limites(reach, ind, z, q, s): # Bed level of each section (mean of the CTDF of the
# neighbouring cells): {(reach, ind): {timestamp: level}}
bed = {}
def set_and_compute_limites(reach, ind, z, q, s, zf=None):
reach.set(ind, timestamp, "Z", z) reach.set(ind, timestamp, "Z", z)
reach.set(ind, timestamp, "Q", q) reach.set(ind, timestamp, "Q", q)
reach.set(ind, timestamp, "V", s) reach.set(ind, timestamp, "V", s)
if zf is not None:
bed.setdefault((reach, ind), {})[timestamp] = zf
profile = reach.profile(ind) profile = reach.profile(ind)
limits = profile.geometry.get_water_limits(z) limits = profile.geometry.get_water_limits(z)
@@ -990,7 +1160,9 @@ class Rubar3(CommandLineSolver):
if line == "": if line == "":
break break
timestamp = float(line) # Saves at the times of TNPROF are marked 'tnprof'
# (rubarbe.f90, SAVEVISU)
timestamp = float(line.split()[0])
ts.add(timestamp) ts.add(timestamp)
logger.info(f"read: profil.{name}: timestamp = {timestamp}") logger.info(f"read: profil.{name}: timestamp = {timestamp}")
@@ -998,7 +1170,7 @@ class Rubar3(CommandLineSolver):
for reach, lm in reachs: for reach, lm in reachs:
# First profile # First profile
h, s, q, z = read_data_line(f) h, s, q, z = read_data_line(f)
set_and_compute_limites(reach, 0, z+h, q, s) set_and_compute_limites(reach, 0, z+h, q, s, z)
# For each profile # For each profile
ind = 1 ind = 1
@@ -1009,7 +1181,8 @@ class Rubar3(CommandLineSolver):
reach, ind, reach, ind,
(pz + z + ph + h) / 2, (pz + z + ph + h) / 2,
(pq + q) / 2, (pq + q) / 2,
(ps + s) / 2 (ps + s) / 2,
(pz + z) / 2
) )
ph = h ph = h
@@ -1020,7 +1193,7 @@ class Rubar3(CommandLineSolver):
# Last profile # Last profile
h, s, q, z = read_data_line(f) h, s, q, z = read_data_line(f)
set_and_compute_limites(reach, ind, z+h, q, s) set_and_compute_limites(reach, ind, z+h, q, s, z)
# Tables and plots use timestamp rows and global profile columns. # Tables and plots use timestamp rows and global profile columns.
timestamps = sorted(ts) timestamps = sorted(ts)
@@ -1030,6 +1203,21 @@ class Rubar3(CommandLineSolver):
profiles.extend(reach.profiles) profiles.extend(reach.profiles)
reach.set_global_index(range(offset, len(profiles))) reach.set_global_index(range(offset, len(profiles)))
# Kept only when the bed moves (sediment transport), so that
# the plots otherwise show the study geometry. The initial
# condition of the study has no bed level: the first one read
# in PROFIL is used
keys = ["Z", "Q", "V"]
moves = any(max(levels.values()) != min(levels.values())
for levels in bed.values())
if moves and len(bed) == len(profiles):
for (reach, ind), levels in bed.items():
first = levels[min(levels)]
for t in timestamps:
first = levels.get(t, first)
reach.set(ind, t, "zfd", first)
keys.append("zfd")
results.set("timestamps", timestamps) results.set("timestamps", timestamps)
results.compute_timestamp_index() results.compute_timestamp_index()
results.set("table", { results.set("table", {
@@ -1037,7 +1225,7 @@ class Rubar3(CommandLineSolver):
[profile.get_ts_key(t, key) for profile in profiles] [profile.get_ts_key(t, key) for profile in profiles]
for t in timestamps for t in timestamps
], dtype=float) ], dtype=float)
for key in ("Z", "Q", "V") for key in keys
}) })
@timer @timer
@@ -1155,8 +1343,74 @@ class Rubar3(CommandLineSolver):
logger_exception(e) logger_exception(e)
return None return None
# Without them, the hydraulic results stay readable
try:
self.read_bed_sections(results, repertory, name)
except Exception as e:
logger.warning(f"Failed to read the bed sections")
logger_exception(e)
return results return results
def read_bed_sections(self, results, repertory, name):
"""Bed sections for the cross section plot of the results window:
- 'bed_points' {global index: [bool, ...]}: the points whose level
changes, strictly between g and d (step 4);
- 'bed_sections' {timestamp: {global index: [z, ...]}}: exact
sections saved at the times of TNPROF when IOVIS=O ('geomac-N',
GEOMAC-I format; rubarbe.f90, SAVEVISU).
Between two exact sections, the plot shifts the points of the last
one by the change of the section bed level 'zfd' since then"""
if "zfd" not in results.get("table") or len(results.river) != 1:
return
profiles = results.river.reach(0).profiles
moving = {}
for profile in profiles:
markers = [self._point_markers.get(p.name.strip(), " ")
for p in profile.geometry.points]
_, _, left, right = self._bed_limits(markers)
moving[profile.global_index] = [
left < i < right for i in range(len(markers))
]
results.set("bed_points", moving)
timestamps = results.get("timestamps")
sections = {}
pattern = re.compile(rf"geomac-\d+\.{re.escape(name)}")
for fname in sorted(os.listdir(repertory)):
if not pattern.fullmatch(fname):
continue
t, levels = self._read_geomac(os.path.join(repertory, fname))
if (len(levels) != len(profiles) or
any(len(z) != len(p.geometry.points)
for z, p in zip(levels, profiles))):
logger.warning(f"{fname}: sections differ from the study "
"geometry, ignored")
continue
# Same save as in PROFIL, with fewer digits (F15.3)
timestamp = min(timestamps, key=lambda ts: abs(ts - t))
if abs(timestamp - t) > 0.01:
continue
sections[timestamp] = {
p.global_index: np.array(z) for p, z in zip(profiles, levels)
}
results.set("bed_sections", sections)
@staticmethod
def _read_geomac(path):
"""Time and level of each point of each section of a GEOMAC file
(GEOMAC-I format: top of the first compartment)"""
with open(path) as f:
lines = iter([line for line in f if line.strip()])
count, time = next(lines).split()[:2]
levels = []
for _ in range(int(count)):
points = int(next(lines).split()[2])
levels.append([float(next(lines)[1:].split()[1])
for _ in range(points)])
return float(time), levels
class RubarBE(Rubar3): class RubarBE(Rubar3):
_type = "rubarbe" _type = "rubarbe"
+36
View File
@@ -286,6 +286,10 @@ class PlotAC(PamhyrPlot):
initial_z = np.asarray(profile.geometry.z(), dtype=float) initial_z = np.asarray(profile.geometry.z(), dtype=float)
result = self.results[self._current_res_id] result = self.results[self._current_res_id]
bed = self._get_bed_from_sections(profile, result, initial_z)
if bed is not None:
return bed
try: try:
sediment_timestamps = sorted(result.get("sediment_timestamps")) sediment_timestamps = sorted(result.get("sediment_timestamps"))
except (KeyError, TypeError): except (KeyError, TypeError):
@@ -324,6 +328,38 @@ class PlotAC(PamhyrPlot):
return initial_z return initial_z
def _get_bed_from_sections(self, profile, result, initial_z):
"""Bed of the solvers that give the bed level of each section at
each time ('zfd') and exact sections at some times
('bed_sections', RubarBE): the last exact section before the
current time, or the study geometry, with its moving points
('bed_points') shifted by the change of the bed level since then.
None for the other results"""
try:
sections = result.get("bed_sections")
moving = result.get("bed_points")[profile.global_index]
except KeyError:
return None
current = profile.get_ts_key(self._current_timestamp, "zfd")
if current is None:
return None
index = profile.global_index
saved = [ts for ts in sorted(sections)
if ts <= self._current_timestamp and index in sections[ts]]
if saved:
base_timestamp = saved[-1]
base = np.asarray(sections[base_timestamp][index], dtype=float)
else:
base_timestamp = min(result.get("timestamps"))
base = initial_z
level = profile.get_ts_key(base_timestamp, "zfd")
if level is None or len(base) != len(moving):
return None
return base + np.where(moving, current - level, 0.0)
def _get_wet_mask(self, profile, elevation): def _get_wet_mask(self, profile, elevation):
result = self.results[self._current_res_id] result = self.results[self._current_res_id]
+43 -5
View File
@@ -49,12 +49,50 @@ class RunSolverTranslate(PamhyrTranslate):
"use a dimensionless critical stress given by the user. " "use a dimensionless critical stress given by the user. "
"Fill it in the parameters (tab Solid Transport)." "Fill it in the parameters (tab Solid Transport)."
) )
self._dict["missing_solid_boundary"] = _translate( self._dict["solid_boundary_downstream"] = _translate(
"Solver", "Solver",
"Export cancelled: sediment transport (TRASED) is enabled but " "Export cancelled: solid boundary condition {name} at node "
"no solid boundary condition is defined at the upstream node " "{node}: RubarBE only reads a solid boundary condition at the "
"of reach(es): {reaches}. Add a solid boundary condition, or " "upstream node. Remove it."
"disable sediment transport in the solver parameters." )
self._dict["solid_boundary_too_long"] = _translate(
"Solver",
"Export cancelled: solid boundary condition {name} at node "
"{node} has {count} points, RubarBE accepts at most {maximum}."
)
self._dict["save_list_invalid"] = _translate(
"Solver",
"Export cancelled: {key}: '{value}' is not a valid value. "
"Separate the values with spaces or semicolons, use a dot as "
"decimal separator, and give the times in seconds or "
"days:hours:minutes:seconds."
)
self._dict["save_list_too_long"] = _translate(
"Solver",
"Export cancelled: {key} has {count} values, RubarBE accepts "
"at most {maximum}."
)
self._dict["save_list_required"] = _translate(
"Solver",
"Export cancelled: {flag} is enabled but {key} is empty. Fill "
"it in the solver parameters (tab Saves), or disable {flag}."
)
self._dict["abshyd_outside"] = _translate(
"Solver",
"Export cancelled: rubarbe_abshyd: abscissa {rk} is outside "
"reach {reach}, whose sections go from {first} to {last}."
)
self._dict["tnprof_outside"] = _translate(
"Solver",
"rubarbe_tnprof: times {times} s are outside the computation, "
"from {tinit} s to {tmax} s. RubarBE saves the earlier ones at "
"the first time steps and never reaches the later ones."
)
self._dict["solid_boundary_invalid"] = _translate(
"Solver",
"Export cancelled: solid boundary condition {name} at node "
"{node} must have D50 > 0 m and sigma >= 1 (1 for a uniform "
"sediment)."
) )
self._dict["multireach_unsupported"] = _translate( self._dict["multireach_unsupported"] = _translate(
"Solver", "Solver",
+31
View File
@@ -262,6 +262,17 @@ def fields():
'Les abscisses ou instants demandés ' 'Les abscisses ou instants demandés '
'doivent être définis dans les données correspondantes.', 'doivent être définis dans les données correspondantes.',
YES_NO) YES_NO)
add('abshyd', 4, 'Abscisses des hydrogrammes (m)',
'Abscisses (PK) où sont enregistrés hydrogrammes et limnigrammes '
'au pas de sauvegarde des hydrogrammes, séparées par des espaces '
'ou des points-virgules ; 100 au plus. Nécessaires si '
'« Hydrogrammes aux abscisses choisies » vaut Oui.',
kind='rk_list')
add('tnprof', 4, 'Instants de sauvegarde des profils (j:h:min:s ou s)',
'Instants séparés par des espaces ou des points-virgules ; 100 au '
'plus. Nécessaires si « Profils aux instants choisis » vaut Oui ; '
'les sorties complémentaires sont aussi écrites à ces instants.',
kind='time_list')
for i in range(1, 4): for i in range(1, 4):
add(f'fdeb{i}', 4, f'Seuil de débit du front {i} (m³/s)', add(f'fdeb{i}', 4, f'Seuil de débit du front {i} (m³/s)',
'Débit pour lequel à chaque abscisse, est enregistré le temps ' 'Débit pour lequel à chaque abscisse, est enregistré le temps '
@@ -298,6 +309,18 @@ def validate(field, value):
return value return value
if field.kind == 'text': if field.kind == 'text':
return value return value
if field.kind in ('rk_list', 'time_list'):
try:
numbers = Rubar3.save_list(value, field.kind == 'time_list')
except ValueError as error:
raise ValueError(
f'Valeur non reconnue : {error}. Séparez les valeurs par '
'des espaces ou des points-virgules, avec un point pour '
'les décimales.') from None
if len(numbers) > Rubar3._max_saves:
raise ValueError(f'{Rubar3._max_saves} valeurs au plus '
'(limite de RubarBE).')
return value
if not value and field.optional: if not value and field.optional:
return value return value
value = value.replace(',', '.').replace('d', 'E').replace('D', 'E') value = value.replace(',', '.').replace('d', 'E').replace('D', 'E')
@@ -364,4 +387,12 @@ def active(field, values):
return values.get('rubarbe_' + source) in accepted return values.get('rubarbe_' + source) in accepted
if key.startswith('fdeb'): if key.startswith('fdeb'):
return values.get('rubarbe_tf_6', 'n').lower() in ('y', 'o') return values.get('rubarbe_tf_6', 'n').lower() in ('y', 'o')
def enabled(flag):
return values.get('rubarbe_' + flag, 'n').lower() in ('y', 'o')
if key == 'abshyd':
return enabled('tf_2')
if key == 'tnprof':
return enabled('tf_4') or enabled('iovis')
return True return True
Binary file not shown.