From d58a509c1d3def03f17758145da52355e3a79154 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Mon, 21 Aug 2023 11:37:01 +0200
Subject: [PATCH] Solver: Add custom command line parameters and solver default
 command line args.

---
 src/Model/River.py                            | 15 ++++++
 .../SolverParameters/SolverParametersList.py  | 11 ++++
 src/Solver/ASolver.py                         | 51 ++++++++++++-------
 src/Solver/Mage.py                            | 16 ++++++
 src/View/RunSolver/Window.py                  |  3 +-
 src/View/SolverParameters/translate.py        |  1 +
 6 files changed, 77 insertions(+), 20 deletions(-)

diff --git a/src/Model/River.py b/src/Model/River.py
index 3041c6c7..91d765ba 100644
--- a/src/Model/River.py
+++ b/src/Model/River.py
@@ -16,6 +16,8 @@
 
 # -*- coding: utf-8 -*-
 
+from tools import flatten
+
 from Model.DB import SQLSubModel
 
 from Model.Network.Node import Node
@@ -382,3 +384,16 @@ class River(Graph, SQLSubModel):
 
     def set_current_reach(self, reach):
         self._current_reach = reach
+
+    def has_sediment(self):
+        has = len(self._sediment_layers) != 0
+        has &= any(
+            filter(
+                lambda p: p.sl != None,
+                flatten(
+                    map(lambda e: e.reach.profiles, self.edges())
+                )
+            )
+        )
+
+        return has
diff --git a/src/Model/SolverParameters/SolverParametersList.py b/src/Model/SolverParameters/SolverParametersList.py
index ce62ff7c..18c45529 100644
--- a/src/Model/SolverParameters/SolverParametersList.py
+++ b/src/Model/SolverParameters/SolverParametersList.py
@@ -167,6 +167,17 @@ class SolverParametersList(SQLSubModel):
     def get(self, index):
         return self._parameters[index]
 
+    def get_by_key(self, key):
+        try:
+            return next(
+                filter(
+                    lambda p: p["name"] == key,
+                    self._parameters
+                )
+            )["value"]
+        except:
+            return None
+
     def set(self, index, new):
         self._parameters[index] = new
         self._status.modified()
diff --git a/src/Solver/ASolver.py b/src/Solver/ASolver.py
index ea268999..186abbbb 100644
--- a/src/Solver/ASolver.py
+++ b/src/Solver/ASolver.py
@@ -86,6 +86,7 @@ class AbstractSolver(object):
             ("all_init_time", "000:00:00:00"),
             ("all_final_time", "999:99:00:00"),
             ("all_timestep", "300.0"),
+            ("all_command_line_arguments", ""),
         ]
 
         return lst
@@ -153,6 +154,17 @@ class AbstractSolver(object):
     def export(self, study, repertory, qlog = None):
         raise NotImplementedMethodeError(self, self.export)
 
+    def cmd_args(self, study):
+        """Return solver command line arguments list
+
+        Returns:
+            Command line arguments list
+        """
+        params = study.river.get_params(self.type)
+        args = params.get_by_key("all_command_line_arguments")
+
+        return args.split(" ")
+
     def input_param(self):
         """Return input command line parameter(s)
 
@@ -190,7 +202,7 @@ class AbstractSolver(object):
             )
         )
 
-    def _format_command(self, cmd, path = ""):
+    def _format_command(self, study, cmd, path = ""):
         """Format command line
 
         Args:
@@ -206,6 +218,7 @@ class AbstractSolver(object):
         cmd = cmd.replace("@path", path.replace(" ", "\ "))
         cmd = cmd.replace("@input", self.input_param())
         cmd = cmd.replace("@dir", self._process.workingDirectory())
+        cmd = cmd.replace("@args", " ".join(self.cmd_args(study)))
 
         logger.debug(f"! {cmd}")
 
@@ -233,13 +246,13 @@ class AbstractSolver(object):
         logger.info(f"! {exe} {args}")
         return exe, args
 
-    def run_input_data_fomater(self):
+    def run_input_data_fomater(self, study):
         if self._cmd_input == "":
-            self._run_next()
+            self._run_next(study)
             return True
 
         cmd = self._cmd_input
-        exe, args = self._format_command(cmd, self._path_input)
+        exe, args = self._format_command(study, cmd, self._path_input)
 
         if not os.path.exists(exe):
             error = f"[ERROR] Path {exe} do not exists"
@@ -252,13 +265,13 @@ class AbstractSolver(object):
 
         return True
 
-    def run_solver(self):
+    def run_solver(self, study):
         if self._cmd_solver == "":
-            self._run_next()
+            self._run_next(study)
             return True
 
         cmd = self._cmd_solver
-        exe, args = self._format_command(cmd, self._path_solver)
+        exe, args = self._format_command(study, cmd, self._path_solver)
 
         if not os.path.exists(exe):
             error = f"[ERROR] Path {exe} do not exists"
@@ -272,13 +285,13 @@ class AbstractSolver(object):
         self._status = STATUS.RUNNING
         return True
 
-    def run_output_data_fomater(self):
+    def run_output_data_fomater(self, study):
         if self._cmd_output == "":
-            self._run_next()
+            self._run_next(study)
             return True
 
         cmd = self._cmd_output
-        exe, args = self._format_command(cmd, self._path_output)
+        exe, args = self._format_command(study, cmd, self._path_output)
 
         if not os.path.exists(exe):
             error = f"[ERROR] Path {exe} do not exists"
@@ -297,29 +310,29 @@ class AbstractSolver(object):
             for x in s.split('\n'):
                 self._output.put(x)
 
-    def _run_next(self):
+    def _run_next(self, study):
         self._step += 1
         if self._step < len(self._runs):
-            res = self._runs[self._step]()
+            res = self._runs[self._step](study)
             if res is not True:
                 self._output.put(res)
         else:
             self._status = STATUS.STOPED
 
-    def _finished(self, exit_code, exit_status):
+    def _finished(self, study, exit_code, exit_status):
         if self._output is not None:
             self._output.put(exit_code)
 
-        self._run_next()
+        self._run_next(study)
 
-    def run(self, process = None, output_queue = None):
+    def run(self, study, process = None, output_queue = None):
         if process is not None:
             self._process = process
         if output_queue is not None:
             self._output = output_queue
 
         self._process.readyRead.connect(self._data_ready)
-        self._process.finished.connect(self._finished)
+        self._process.finished.connect(lambda c, s: self._finished(study, c, s))
 
         self._runs = [
             self.run_input_data_fomater,
@@ -328,7 +341,7 @@ class AbstractSolver(object):
         ]
         self._step = 0
         # Run first step
-        res = self._runs[0]()
+        res = self._runs[0](study)
         if res is not True:
             self._output.put(res)
 
@@ -340,14 +353,14 @@ class AbstractSolver(object):
         self._status = STATUS.STOPED
         return True
 
-    def start(self, process = None):
+    def start(self, study, process = None):
         if _signal:
             if self._status == STATUS.PAUSED:
                 os.kill(self._process.pid(), SIGCONT)
                 self._status = STATUS.RUNNING
                 return True
 
-        self.run(process)
+        self.run(study, process)
         return True
 
     def pause(self):
diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py
index 77cc83d3..5e7909d7 100644
--- a/src/Solver/Mage.py
+++ b/src/Solver/Mage.py
@@ -97,6 +97,14 @@ class Mage(AbstractSolver):
     # Export #
     ##########
 
+    def cmd_args(self, study):
+        l = super(Mage, self).cmd_args(study)
+
+        l.append("-r")
+        l.append("-fp=1")
+
+        return l
+
     def input_param(self):
         return "0.REP"
 
@@ -440,6 +448,14 @@ class Mage8(Mage):
     # Export #
     ##########
 
+    def cmd_args(self, study):
+        l = super(Mage8, self).cmd_args(study)
+
+        if study.river.has_sediment():
+            l.append("-c=3")
+
+        return l
+
     @timer
     def _export_PAR(self, study, repertory, qlog = None):
         files = []
diff --git a/src/View/RunSolver/Window.py b/src/View/RunSolver/Window.py
index 872d72ef..c307157a 100644
--- a/src/View/RunSolver/Window.py
+++ b/src/View/RunSolver/Window.py
@@ -147,6 +147,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
 
         self._log(f" *** Run solver {self._solver.name}", color="blue")
         self._solver.run(
+            study,
             process = self._process,
             output_queue = self._output
         )
@@ -220,7 +221,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
 
         self._log(" *** Start", color="blue")
         self._results = None
-        self._solver.start(self._process)
+        self._solver.start(self._study, process = self._process)
 
         self.find(QAction, "action_start").setEnabled(False)
         if _signal:
diff --git a/src/View/SolverParameters/translate.py b/src/View/SolverParameters/translate.py
index 3d07e6b3..2508629e 100644
--- a/src/View/SolverParameters/translate.py
+++ b/src/View/SolverParameters/translate.py
@@ -55,6 +55,7 @@ def init():
         "all_init_time": _translate("SolverParameters", "Initial time (jj:hh:mm:ss)"),
         "all_final_time": _translate("SolverParameters", "Final time (jj:hh:mm:ss)"),
         "all_timestep": _translate("SolverParameters", "Timestep (second)"),
+        "all_command_line_arguments": _translate("SolverParameters", "Command line arguments"),
         # Mage specific parameters
         "mage_min_timestep": _translate("SolverParameters", "Minimum timestep (second)"),
         "mage_timestep_tra": _translate("SolverParameters", "Time step of writing on .TRA"),
-- 
GitLab