From eebb7d2893f17a28827d63f2ae1503b2e7eca2d2 Mon Sep 17 00:00:00 2001
From: Federico Montesino Pouzols <federico.montesino-pouzols@stfc.ac.uk>
Date: Mon, 24 Aug 2015 18:41:02 +0100
Subject: [PATCH] do focusing in presenter, re #13375

---
 .../EnggDiffractionPresWorker.h               |  30 ++-
 .../EnggDiffractionPresenter.h                |  35 ++-
 .../IEnggDiffractionPresenter.h               |   1 +
 .../EnggDiffractionPresenter.cpp              | 252 +++++++++++++++++-
 4 files changed, 302 insertions(+), 16 deletions(-)

diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresWorker.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresWorker.h
index 1765a9a774d..d241b2dd136 100644
--- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresWorker.h
+++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresWorker.h
@@ -38,11 +38,18 @@ class EnggDiffWorker : public QObject {
   Q_OBJECT
 
 public:
-  EnggDiffWorker(EnggDiffractionPresenter *pres,
-                  const std::string &outFilename, const std::string &vanNo,
-                  const std::string &ceriaNo)
+  /// for calibration
+  EnggDiffWorker(EnggDiffractionPresenter *pres, const std::string &outFilename,
+                 const std::string &vanNo, const std::string &ceriaNo)
       : m_pres(pres), m_outFilename(outFilename), m_vanNo(vanNo),
-        m_ceriaNo(ceriaNo) {}
+        m_ceriaNo(ceriaNo), m_bank(-1) {}
+
+  /// for focusing
+  EnggDiffWorker(EnggDiffractionPresenter *pres, const std::string &outDir,
+                 const std::string &outFilename, const std::string &runNo,
+                 int bank)
+      : m_pres(pres), m_outFilename(outFilename), m_runNo(runNo),
+        m_outDir(outDir), m_bank(bank) {}
 
 private slots:
 
@@ -55,6 +62,15 @@ private slots:
     emit finished();
   }
 
+  /**
+   * Focus a run. You must connect this from a thread started()
+   * signal.
+   */
+  void focus() {
+    m_pres->doFocusRun(m_outDir, m_outFilename, m_runNo, m_bank);
+    emit finished();
+  }
+
 signals:
   void finished();
 
@@ -62,6 +78,12 @@ private:
   EnggDiffractionPresenter *m_pres;
   /// parameters for calibration
   std::string m_outFilename, m_vanNo, m_ceriaNo;
+  /// sample run to process
+  const std::string m_runNo;
+  /// Output directory
+  const std::string m_outDir;
+  /// instrument bank
+  int m_bank;
 };
 
 } // namespace CustomInterfaces
diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresenter.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresenter.h
index 91841f1b51c..0ad0aa0da77 100644
--- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresenter.h
+++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresenter.h
@@ -58,10 +58,14 @@ public:
 
   virtual void notify(IEnggDiffractionPresenter::Notification notif);
 
-  // this is the calibration hard work that a worker / thread will run
+  /// the calibration hard work that a worker / thread will run
   void doNewCalibration(const std::string &outFilename,
                         const std::string &vanNo, const std::string &ceriaNo);
 
+  /// the focusing hard work that a worker / thread will run
+  void doFocusRun(const std::string &dir, const std::string &outFilename,
+                  const std::string &runNo, int bank);
+
 protected:
   void initialize();
 
@@ -71,14 +75,18 @@ protected:
   void processStart();
   void processLoadExistingCalib();
   void processCalcCalib();
+  void processFocusRun();
   void processLogMsg();
   void processInstChange();
   void processShutDown();
 
 protected slots:
   void calibrationFinished();
+  void focusingFinished();
 
 private:
+  /// @name Calibration related private methods
+  //@{
   void inputChecksBeforeCalibrate(const std::string &newVanNo,
                                   const std::string &newCeriaNo);
 
@@ -89,15 +97,32 @@ private:
                               std::string &vanNo, std::string &ceriaNo);
 
   // this may need to be mocked up in tests
-  virtual void startAsynCalibWorker(const std::string &outFilename,
-                                    const std::string &vanNo,
-                                    const std::string &ceriaNo);
+  virtual void startAsyncCalibWorker(const std::string &outFilename,
+                                     const std::string &vanNo,
+                                     const std::string &ceriaNo);
 
   void doCalib(const EnggDiffCalibSettings &cs, const std::string &vanNo,
                const std::string &ceriaNo, const std::string &outFilename);
 
   std::string buildCalibrateSuggestedFilename(const std::string &vanNo,
                                               const std::string &ceriaNo) const;
+  //@}
+
+  /// @name Focusing related private methods
+  //@{
+  /// this may also need to be mocked up in tests
+  virtual void startAsyncFocusWorker(const std::string &dir,
+                                     const std::string &outFilename,
+                                     const std::string &runNo, int bank);
+
+  void inputChecksBeforeFocus(const std::string &runNo, int bank);
+
+  std::string outputFocusFilename(const std::string &runNo, int bank);
+
+  void doFocusing(const EnggDiffCalibSettings &cs,
+                  const std::string &fullFilename, const std::string &runNo,
+                  int bank);
+  //@}
 
   void loadOrCalcVanadiumWorkspaces(
       const std::string &vanNo, const std::string &inputDirCalib,
@@ -129,6 +154,8 @@ private:
 
   /// true if the last calibration completed successfully
   bool m_calibFinishedOK;
+  /// true if the last focusing completed successfully
+  bool m_focusFinishedOK;
 
   /// Associated view for this presenter (MVP pattern)
   IEnggDiffractionView *const m_view;
diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/IEnggDiffractionPresenter.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/IEnggDiffractionPresenter.h
index 7fd0ea69661..42020a4c53e 100644
--- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/IEnggDiffractionPresenter.h
+++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/EnggDiffraction/IEnggDiffractionPresenter.h
@@ -41,6 +41,7 @@ public:
     Start,                 ///< Start and setup interface
     LoadExistingCalib,     ///< Load a calibration already availble on disk
     CalcCalib,             ///< Calculate a (new) calibration
+    FocusRun,              ///< Focus a run file
     LogMsg,                ///< need to send a message to the Mantid log system
     InstrumentChange,      ///< Instrument selection updated
     ShutDown               ///< closing the interface
diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/EnggDiffraction/EnggDiffractionPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/EnggDiffraction/EnggDiffractionPresenter.cpp
index cb75a9392c9..ebaf2895e55 100644
--- a/Code/Mantid/MantidQt/CustomInterfaces/src/EnggDiffraction/EnggDiffractionPresenter.cpp
+++ b/Code/Mantid/MantidQt/CustomInterfaces/src/EnggDiffraction/EnggDiffractionPresenter.cpp
@@ -30,7 +30,7 @@ const std::string EnggDiffractionPresenter::g_enginxStr = "ENGINX";
 const bool EnggDiffractionPresenter::g_askUserCalibFilename = false;
 
 EnggDiffractionPresenter::EnggDiffractionPresenter(IEnggDiffractionView *view)
-    : m_workerThread(NULL), m_calibFinishedOK(false),
+    : m_workerThread(NULL), m_calibFinishedOK(false), m_focusFinishedOK(false),
       m_view(view) /*, m_model(new EnggDiffractionModel()), */ {
   if (!m_view) {
     throw std::runtime_error(
@@ -78,6 +78,10 @@ void EnggDiffractionPresenter::notify(
     processCalcCalib();
     break;
 
+  case IEnggDiffractionPresenter::FocusRun:
+    processFocusRun();
+    break;
+
   case IEnggDiffractionPresenter::LogMsg:
     processLogMsg();
     break;
@@ -130,11 +134,35 @@ void EnggDiffractionPresenter::processCalcCalib() {
 
   const std::string outFilename = outputCalibFilename(vanNo, ceriaNo);
 
-  m_view->enableCalibrateActions(false);
+  m_view->enableCalibrateAndFocusActions(false);
   // alternatively, this would be GUI-blocking:
   // doNewCalibration(outFilename, vanNo, ceriaNo);
   // calibrationFinished()
-  startAsynCalibWorker(outFilename, vanNo, ceriaNo);
+  startAsyncCalibWorker(outFilename, vanNo, ceriaNo);
+}
+
+void EnggDiffractionPresenter::processFocusRun() {
+  std::string runNo = m_view->focusingRunNo();
+  int bank = m_view->focusingBank();
+  try {
+    inputChecksBeforeFocus(runNo, bank);
+  } catch (std::invalid_argument &ia) {
+    m_view->userWarning("Error in the inputs required to focus a run",
+                        ia.what());
+    return;
+  }
+
+  g_log.notice() << "EnggDiffraction GUI: starting new focusing. This may take "
+                    "some seconds... " << std::endl;
+
+  const std::string focusDir = m_view->focusingDir();
+  const std::string outFilename = outputFocusFilename(runNo, bank);
+
+  m_view->enableCalibrateAndFocusActions(false);
+  // GUI-blocking alternative:
+  // doFocusRun(outFielname, runNo, bank)
+  // focusingFinished()
+  startAsyncFocusWorker(focusDir, outFilename, runNo, bank);
 }
 
 void EnggDiffractionPresenter::processLogMsg() {
@@ -296,16 +324,18 @@ void EnggDiffractionPresenter::parseCalibrateFilename(const std::string &path,
 }
 
 /**
- * Start the calibration work without blocking the GUI
+ * Start the calibration work without blocking the GUI. This uses
+ * connect for Qt signals/slots so that it runs well with the Qt event
+ * loop. Because of that this class needs to be a Q_OBJECT.
  *
  * @param outFilename name for the output GSAS calibration file
  * @param vanNo vanadium run number
  * @param ceriaNo ceria run number
  */
 void
-EnggDiffractionPresenter::startAsynCalibWorker(const std::string &outFilename,
-                                               const std::string &vanNo,
-                                               const std::string &ceriaNo) {
+EnggDiffractionPresenter::startAsyncCalibWorker(const std::string &outFilename,
+                                                const std::string &vanNo,
+                                                const std::string &ceriaNo) {
   delete m_workerThread;
   m_workerThread = new QThread(this);
   EnggDiffWorker *worker =
@@ -372,7 +402,7 @@ void EnggDiffractionPresenter::calibrationFinished() {
   if (!m_view)
     return;
 
-  m_view->enableCalibrateActions(true);
+  m_view->enableCalibrateAndFocusActions(true);
   if (!m_calibFinishedOK) {
     g_log.warning() << "The cablibration did not finished correctly."
                     << std::endl;
@@ -512,6 +542,212 @@ void EnggDiffractionPresenter::doCalib(const EnggDiffCalibSettings &cs,
                  << std::endl;
 }
 
+/**
+ * @TODO
+ *
+ */
+void EnggDiffractionPresenter::inputChecksBeforeFocus(const std::string &runNo,
+                                                      int bank) {}
+
+/**
+ * @TODO
+ *
+ */
+std::string
+EnggDiffractionPresenter::outputFocusFilename(const std::string &runNo,
+                                              int bank) {
+  const std::string instStr = m_view->currentInstrument();
+
+  return instStr + runNo + "_focused";
+}
+
+/**
+ * Start the focusing algorithm(s) without blocking the GUI. This is
+ * based on Qt connect / signals-slots so that it goes in sync with
+ * the Qt event loop. For that reason this class needs to be a
+ * Q_OBJECT.
+ *
+ * @param dir directory (full path) for the focused output files
+ * @param outFilename full name for the output focused run
+ * @param runNo input run number
+ * @param bank instrument bank to focus
+ */
+void EnggDiffractionPresenter::startAsyncFocusWorker(
+    const std::string &dir, const std::string &outFilename,
+    const std::string &runNo, int bank) {
+  delete m_workerThread;
+  m_workerThread = new QThread(this);
+  EnggDiffWorker *worker =
+      new EnggDiffWorker(this, dir, outFilename, runNo, bank);
+  worker->moveToThread(m_workerThread);
+
+  connect(m_workerThread, SIGNAL(started()), worker, SLOT(focus()));
+  connect(worker, SIGNAL(finished()), this, SLOT(focusingFinished()));
+  // early delete of thread and worker
+  connect(m_workerThread, SIGNAL(finished()), m_workerThread,
+          SLOT(deleteLater()), Qt::DirectConnection);
+  connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
+  m_workerThread->start();
+}
+
+/**
+ * Produce a new focused output file. This is what threads/workers
+ * should use to run the calculations required to process a 'focus'
+ * push or similar from the user.
+ *
+ * @param dir directory (full path) for the output focused files
+ * @param outFilename name for the output focused file
+ * @param runNo input run number
+ * @param bank bank number for the focusing
+ */
+void EnggDiffractionPresenter::doFocusRun(const std::string &dir,
+                                          const std::string &outFilename,
+                                          const std::string &runNo, int bank) {
+  g_log.notice() << "Generating new focused file (bank " +
+                        boost::lexical_cast<std::string>(bank) + ") for run " +
+                        runNo + " into: " << outFilename << std::endl;
+
+  Poco::Path fpath(dir);
+  const std::string fullFilename = fpath.append(fpath).toString();
+
+  // TODO: this is almost 100% common with doNewCalibrate() - refactor
+  EnggDiffCalibSettings cs = m_view->currentCalibSettings();
+  Mantid::Kernel::ConfigServiceImpl &conf =
+      Mantid::Kernel::ConfigService::Instance();
+  const std::vector<std::string> tmpDirs = conf.getDataSearchDirs();
+  // in principle, the run files will be found from 'DirRaw', and the
+  // pre-calculated Vanadium corrections from 'DirCalib'
+  if (!cs.m_inputDirCalib.empty() && Poco::File(cs.m_inputDirCalib).exists()) {
+    conf.appendDataSearchDir(cs.m_inputDirCalib);
+  }
+  if (!cs.m_inputDirRaw.empty() && Poco::File(cs.m_inputDirRaw).exists()) {
+    conf.appendDataSearchDir(cs.m_inputDirRaw);
+  }
+
+  try {
+    doFocusing(cs, fullFilename, runNo, bank);
+    m_focusFinishedOK = true;
+  } catch (std::runtime_error &) {
+    g_log.error() << "The focusing calculations failed. One of the algorithms"
+                     "did not execute correctly. See log messages for details."
+                  << std::endl;
+  } catch (std::invalid_argument &) {
+    g_log.error()
+        << "The focusing failed. Some input properties were not valid. "
+           "See log messages for details. " << std::endl;
+  }
+
+  // restore initial data search paths
+  conf.setDataSearchDirs(tmpDirs);
+}
+
+/**
+ * Method (Qt slot) to call when the focusing work has finished,
+ * possibly from a separate thread but sometimes not (as in this
+ * presenter class' test).
+ */
+void EnggDiffractionPresenter::focusingFinished() {
+  if (!m_view)
+    return;
+
+  m_view->enableCalibrateAndFocusActions(true);
+  if (!m_focusFinishedOK) {
+    g_log.warning() << "The cablibration did not finished correctly."
+                    << std::endl;
+  } else {
+    g_log.notice() << "Focusing finished - focused run ready." << std::endl;
+  }
+  if (m_workerThread) {
+    delete m_workerThread;
+    m_workerThread = NULL;
+  }
+}
+
+/**
+ * Focuses a run, produces a focused workspace, and saves it into a
+ * file.
+ *
+ * @param cs user settings for calibration (this does not calibrate but
+ * uses calibration input files such as vanadium runs
+ *
+ * @param fullFilename full paht for the output (focused) filename
+ * @param runNo input run to focus
+ * @param bank instrument bank to focus
+ */
+void EnggDiffractionPresenter::doFocusing(const EnggDiffCalibSettings &cs,
+                                          const std::string &fullFilename,
+                                          const std::string &runNo, int bank) {
+  ITableWorkspace_sptr vanIntegWS;
+  MatrixWorkspace_sptr vanCurvesWS;
+  MatrixWorkspace_sptr inWS;
+
+  const std::string vanNo = m_view->currentVanadiumNo();
+  loadOrCalcVanadiumWorkspaces(vanNo, cs.m_inputDirCalib, vanIntegWS,
+                               vanCurvesWS, cs.m_forceRecalcOverwrite);
+
+  const std::string inWSName = "engggui_focusing_input_ws";
+  const std::string instStr = m_view->currentInstrument();
+  try {
+    auto load = Algorithm::fromString("Load");
+    load->initialize();
+    load->setPropertyValue("Filename", instStr + runNo);
+    load->setPropertyValue("OutputWorkspace", inWSName);
+    load->execute();
+
+    AnalysisDataServiceImpl &ADS = Mantid::API::AnalysisDataService::Instance();
+    inWS = ADS.retrieveWS<MatrixWorkspace>(inWSName);
+  } catch (std::runtime_error &re) {
+    g_log.error()
+        << "Error while loading sample data for focusing. "
+           "Could not run the algorithm Load succesfully for the focusing "
+           "sample (run number: " +
+               runNo + "). Error description: " + re.what() +
+               " Please check also the previous log messages for details.";
+    throw;
+  }
+
+  const std::string outWSName = "enggui_focusing_output_ws";
+  try {
+    auto alg = Algorithm::fromString("EnggFocus");
+    alg->initialize();
+    alg->setProperty("InputWorkspace", inWSName);
+    alg->setProperty("OutputWorkspace", outWSName);
+    alg->setProperty("VanIntegrationWorkspace", vanIntegWS);
+    alg->setProperty("VanCurvesWorkspace", vanCurvesWS);
+    alg->setPropertyValue("Bank", boost::lexical_cast<std::string>(bank));
+    alg->setPropertyValue("OutputParametersTableName",
+                          "engggui_calibration_bank_" +
+                              boost::lexical_cast<std::string>(bank));
+    // TODO: use detector positions (from calibrate full) when available
+    // alg->setProperty(DetectorPositions, TableWorkspace)
+    alg->execute();
+  } catch (std::runtime_error &re) {
+    g_log.error() << "Error in calibration. ",
+        "Could not run the algorithm EnggCalibrate succesfully for bank " +
+            boost::lexical_cast<std::string>(bank) + ". Error description: " +
+            re.what() + " Please check also the log messages for details.";
+    throw;
+  }
+
+  g_log.notice() << "Produced focused workspace: " << outWSName << std::endl;
+
+  try {
+    auto alg = Algorithm::fromString("SaveNexus");
+    alg->initialize();
+    alg->setPropertyValue("InputWorkspace", outWSName);
+    alg->setPropertyValue("Filename", fullFilename);
+    alg->execute();
+  } catch (std::runtime_error &re) {
+    g_log.error() << "Error in calibration. ",
+        "Could not run the algorithm EnggCalibrate succesfully for bank " +
+            boost::lexical_cast<std::string>(bank) + ". Error description: " +
+            re.what() + " Please check also the log messages for details.";
+    throw;
+  }
+  g_log.notice() << "Saved focused workspace as file: " << fullFilename
+                 << std::endl;
+}
+
 /**
  * Produce the two workspaces that are required to apply Vanadium
  * corrections. Try to load them if precalculated results are
-- 
GitLab