diff --git a/src/Model/SolverParameters/SolverParametersList.py b/src/Model/SolverParameters/SolverParametersList.py index d61787d7..635b1014 100644 --- a/src/Model/SolverParameters/SolverParametersList.py +++ b/src/Model/SolverParameters/SolverParametersList.py @@ -315,6 +315,14 @@ class SolverParametersList(PamhyrModelList): n._lst = list(map(lambda t: t[1], sorted(lst))) + # Parameters added to the solver after the study was saved + names = {p.name for p in n._lst} + n._lst += [ + Parameter(name=name, value=value, status=status) + for name, value in st.default_parameters() + if name not in names + ] + new[solver] = n return new diff --git a/src/Solver/RubarBE.py b/src/Solver/RubarBE.py index 2a87268a..939df3eb 100644 --- a/src/Solver/RubarBE.py +++ b/src/Solver/RubarBE.py @@ -92,6 +92,7 @@ class Rubar3(CommandLineSolver): ("rubarbe_sediment_mult_3", ""), ("rubarbe_sediment_mult_4", ""), ("rubarbe_sediment_mult_5", ""), + ("rubarbe_sediment_tcadim", "0.047"), ("rubarbe_sediment_visc", "1.3E-06"), ("rubarbe_sediment_opts", "3"), ("rubarbe_sediment_odchar", "0"), @@ -200,7 +201,7 @@ class Rubar3(CommandLineSolver): _ts_fields = ( ("ros", "real"), ("por", "real"), ("dcharg", "real"), ("halfa", "real"), ("mult_1", "multipliers"), - ("visc", "real"), ("opts", "law"), ("odchar", "integer"), + ("visc", "visc"), ("opts", "law"), ("odchar", "integer"), ("unisol", "integer"), ("typdef", "integer"), ("depot", "depot"), ("choixc", "integer"), ("option", "options"), ("capsol", "integer"), @@ -208,6 +209,28 @@ class Rubar3(CommandLineSolver): ("defond", "integer"), ("varcons", "integer"), ) + # rubar_init.f90 (inits): laws whose critical stress may come from the + # Shields diagram, and laws without critical stress + _shields_laws = {1, 10, 11, 12, 14, 25, 26} + _no_threshold_laws = {2, 3, 8, 13, 18, 19, 22, 23, 24} + _brownlie_law = 16 + + @classmethod + def tcadim_required(cls, opts, option): + """True if RubarBE reads the dimensionless critical stress TCADIM + for transport law OPTS and options OPTION (notice §3.10)""" + try: + law = int(opts) + except ValueError: + return False + + shields = len(option) == 3 and option[1] != "1" + if shields and law in cls._shields_laws: + return False + if law in cls._no_threshold_laws or law == cls._brownlie_law: + return False + return True + @staticmethod def _reaches_without_solid_boundary(study): river = study.river @@ -344,6 +367,26 @@ class Rubar3(CommandLineSolver): value = self._required(values, key) if kind == "real": result = self._real(value, key) + elif kind == "visc": + # Notice annex 1: TCADIM then VISC, read as (A50, A20) then + # two F10.0. A single value would be read as TCADIM, with + # VISC = 0 (rubar_init.f90, inits). + if Decimal(self._real(value, key)) <= 0: + raise ValueError(f"{key}: expected a positive viscosity") + tcadim_key = prefix + "tcadim" + tcadim = values.get(tcadim_key, "") + if tcadim: + result = self._real(tcadim, tcadim_key) + elif self.tcadim_required(values.get(prefix + "opts", ""), + values.get(prefix + "option", "")): + message = trad["missing_tcadim"].format(key=tcadim_key) + logger.warning(message) + if qlog is not None: + qlog.put(trad["warning"].format(message=message)) + return False + else: + result = " " * 10 + result += self._real(value, key) elif kind == "multipliers": result = self._real(value, key) extras = [values.get(prefix + f"mult_{i}", "") diff --git a/src/View/RunSolver/Translate.py b/src/View/RunSolver/Translate.py index ebffb9d8..29ce3b97 100644 --- a/src/View/RunSolver/Translate.py +++ b/src/View/RunSolver/Translate.py @@ -43,6 +43,12 @@ class RunSolverTranslate(PamhyrTranslate): "Export cancelled: REGIME=1 is incompatible with CONDAM=0. " "A reflecting upstream boundary cannot impose the water depth." ) + self._dict["missing_tcadim"] = _translate( + "Solver", + "Export cancelled: {key}: the selected transport law and options " + "use a dimensionless critical stress given by the user. " + "Fill it in the parameters (tab Solid Transport)." + ) self._dict["missing_solid_boundary"] = _translate( "Solver", "Export cancelled: sediment transport (TRASED) is enabled but " diff --git a/src/View/SolverParameters/RubarFields.py b/src/View/SolverParameters/RubarFields.py index a97f12be..6f3ffc5b 100644 --- a/src/View/SolverParameters/RubarFields.py +++ b/src/View/SolverParameters/RubarFields.py @@ -156,10 +156,15 @@ def fields(): add('sediment_option', 2, 'Calcul de la contrainte critique', 'Trois choix indépendants, enregistrés ensemble dans OPTION.', kind='options') - add('sediment_visc', 2, 'Viscosité ou seuil critique', - 'Selon la configuration : seuil critique adimensionnel fourni, ou ' - 'viscosité cinématique de l’eau. À 15 °C : environ 1,30 × 10⁻⁶ m²/s. ' - 'Un changement de mode ne convertit pas la valeur.', positive=True) + add('sediment_tcadim', 2, 'Seuil critique adimensionnel (—)', + 'Contrainte critique adimensionnelle (TCADIM) fournie par ' + 'l’utilisateur, par exemple 0,047 pour Meyer-Peter et Müller ou 0,15 ' + 'pour Recking et al. (2008). Utilisée lorsque le seuil n’est pas ' + 'calculé par le diagramme de Shields et que la loi en a besoin.', + positive=True, optional=True) + add('sediment_visc', 2, 'Viscosité cinématique de l’eau (m²/s)', + 'Viscosité cinématique de l’eau, variable avec la température. ' + 'À 15 °C : environ 1,30 × 10⁻⁶ m²/s.', positive=True) choice('sediment_choixc', 2, 'Calcul de la contrainte hydrodynamique de ' 'cisaillement', 'MPC : méthode des perpendiculaires confondues (notice §8.5).', @@ -327,6 +332,10 @@ def active(field, values): return False if key == 'tiopdt': return values.get('rubarbe_iopdt', '').lower() == 'm' + if key == 'sediment_tcadim': + return Rubar3.tcadim_required( + values.get('rubarbe_sediment_opts', ''), + values.get('rubarbe_sediment_option', '')) if key in ('ros', 'dm', 'segma'): return (not transport and values.get('rubarbe_optfpc', '0') not in ('', '0')) diff --git a/src/View/SolverParameters/RubarForm.py b/src/View/SolverParameters/RubarForm.py index 2a3a59c9..5c594652 100644 --- a/src/View/SolverParameters/RubarForm.py +++ b/src/View/SolverParameters/RubarForm.py @@ -296,16 +296,6 @@ class RubarForm(QWidget): self._refreshing = False def _update_context(self, values): - option = values.get('rubarbe_sediment_option', '') - label = self.labels.get('rubarbe_sediment_visc') - if label is not None: - if len(option) == 3 and all(c in '12' for c in option): - title = ('Seuil critique adimensionnel (—)' if option[1] == '1' - else 'Viscosité cinématique de l’eau (m²/s)') - label.setText(tr(title)) - else: - label.setText(tr( - 'Viscosité / seuil : préciser les trois choix')) warnings = [] if not self.editable: warnings.append(tr('Étude en lecture seule.')) @@ -334,15 +324,21 @@ class RubarForm(QWidget): warnings.append(tr('La fin du calcul doit suivre son début.')) except (KeyError, ValueError): pass - if transport and len(option) == 3 and option[1] == '2': + if transport: try: if float(values.get('rubarbe_sediment_visc', '0')) > 0.001: warnings.append(tr( - 'Viscosité élevée pour de l’eau : vérifiez la valeur, ' - 'qui n’a pas été convertie lors du changement ' - 'de mode.')) + 'Viscosité élevée pour de l’eau : vérifiez la valeur. ' + 'Le seuil critique adimensionnel se saisit dans son ' + 'propre champ.')) except ValueError: pass + if (FIELDS['rubarbe_sediment_tcadim'].key in self.editors and + active(FIELDS['rubarbe_sediment_tcadim'], values) and + not values.get('rubarbe_sediment_tcadim', '').strip()): + warnings.append(tr( + 'La loi et les options choisies utilisent un seuil ' + 'critique adimensionnel : renseignez-le.')) self.notice.setText('\n'.join(warnings)) self.notice.setVisible(bool(warnings))