From 98d4b45b5d21fa43d8255f131358e3d74872301e Mon Sep 17 00:00:00 2001
From: Verena Reimund <reimund@ill.eu>
Date: Tue, 17 Apr 2018 11:23:08 +0200
Subject: [PATCH] Add input validation & clean doxygen commands

Refs #22197
---
 .../inc/MantidAlgorithms/Stitch1D.h           |  2 +
 Framework/Algorithms/src/Stitch1D.cpp         | 83 ++++++++++---------
 Framework/Algorithms/test/Stitch1DTest.h      | 65 ++++++++++-----
 3 files changed, 89 insertions(+), 61 deletions(-)

diff --git a/Framework/Algorithms/inc/MantidAlgorithms/Stitch1D.h b/Framework/Algorithms/inc/MantidAlgorithms/Stitch1D.h
index beaba343583..30b3c669624 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/Stitch1D.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/Stitch1D.h
@@ -48,6 +48,8 @@ public:
   }
   /// Does the x-axis have non-zero errors
   bool hasNonzeroErrors(Mantid::API::MatrixWorkspace_sptr ws);
+  /// Cross-check properties with each other @see IAlgorithm::validateInputs
+  std::map<std::string, std::string> validateInputs() override;
 
 private:
   /// Helper typedef. For storing indexes of special values per spectra per
diff --git a/Framework/Algorithms/src/Stitch1D.cpp b/Framework/Algorithms/src/Stitch1D.cpp
index 44df37e45a2..45a42a7540f 100644
--- a/Framework/Algorithms/src/Stitch1D.cpp
+++ b/Framework/Algorithms/src/Stitch1D.cpp
@@ -3,25 +3,24 @@
 #include "MantidAPI/WorkspaceProperty.h"
 #include "MantidAPI/MatrixWorkspace.h"
 #include "MantidAPI/WorkspaceFactory.h"
+#include "MantidHistogramData/HistogramE.h"
 #include "MantidHistogramData/HistogramX.h"
 #include "MantidHistogramData/HistogramY.h"
-#include "MantidHistogramData/HistogramE.h"
 #include "MantidKernel/ArrayProperty.h"
 #include "MantidKernel/BoundedValidator.h"
 #include "MantidKernel/MultiThreaded.h"
 #include "MantidKernel/PropertyWithValue.h"
 #include "MantidKernel/RebinParamsValidator.h"
 
+#include <algorithm>
 #include <boost/tuple/tuple.hpp>
 #include <boost/format.hpp>
-#include <boost/algorithm/string.hpp>
 #include <boost/math/special_functions.hpp>
-#include <algorithm>
 
-using namespace Mantid::Kernel;
 using namespace Mantid::API;
-using Mantid::HistogramData::HistogramY;
+using namespace Mantid::Kernel;
 using Mantid::HistogramData::HistogramE;
+using Mantid::HistogramData::HistogramY;
 
 namespace {
 
@@ -37,9 +36,7 @@ bool isNonzero(double i) { return (0 != i); }
 namespace Mantid {
 namespace Algorithms {
 
-/**
- * Range tolerance
- *
+/** Range tolerance
  * This is required for machine precision reasons. Used to adjust StartOverlap
  *and EndOverlap so that they are
  * inclusive of bin boundaries if they are sitting ontop of the bin boundaries.
@@ -48,8 +45,7 @@ const double Stitch1D::range_tolerance = 1e-9;
 // Register the algorithm into the AlgorithmFactory
 DECLARE_ALGORITHM(Stitch1D)
 
-/**
- * Zero out all y and e data that is not in the region a1 to a2.
+/** Zero out all y and e data that is not in the region a1 to a2.
  * @param a1 : Zero based bin index (first one)
  * @param a2 : Zero based bin index (last one inclusive)
  * @param source : Workspace providing the source data.
@@ -87,8 +83,8 @@ MatrixWorkspace_sptr Stitch1D::maskAllBut(int a1, int a2,
   return product;
 }
 
-/**
- * Mask out data in the region between a1 and a2 with zeros. Operation performed
+/** Mask out data in the region between a1 and a2 with zeros. Operation
+ * performed
  * on the original workspace
  * @param a1 : start position in X
  * @param a2 : end position in X
@@ -123,7 +119,8 @@ void Stitch1D::init() {
                   "LHS input workspace.");
   declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
                       "RHSWorkspace", "", Direction::Input),
-                  "RHS input workspace.");
+                  "RHS input workspace, must be same type as LHSWorkspace "
+                  "(histogram or point data).");
   declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
                       "OutputWorkspace", "", Direction::Output),
                   "Output stitched workspace.");
@@ -158,7 +155,21 @@ void Stitch1D::init() {
                   "The actual used value for the scaling factor.");
 }
 
-/**Gets the start of the overlapping region
+/** Validate the algorithm's properties.
+ * @return A map of porperty names and their issues.
+ */
+std::map<std::string, std::string> Stitch1D::validateInputs(void) {
+  std::map<std::string, std::string> issues;
+  MatrixWorkspace_sptr lhs = getProperty("LHSWorkspace");
+  MatrixWorkspace_sptr rhs = getProperty("RHSWorkspace");
+  if (lhs->isHistogramData() && !rhs->isHistogramData())
+    issues["RHSWorkspace"] = "Must be a histogram like LHSWorkspace.";
+  if (!lhs->isHistogramData() && rhs->isHistogramData())
+    issues["RHSWorkspace"] = "Must be point data like LHSWorkspace.";
+  return issues;
+}
+
+/** Gets the start of the overlapping region
  @param intesectionMin :: The minimum possible value for the overlapping region
  to inhabit
  @param intesectionMax :: The maximum possible value for the overlapping region
@@ -189,7 +200,7 @@ double Stitch1D::getStartOverlap(const double &intesectionMin,
   return startOverlapVal;
 }
 
-/**Gets the end of the overlapping region
+/** Gets the end of the overlapping region
  @param intesectionMin :: The minimum possible value for the overlapping region
  to inhabit
  @param intesectionMax :: The maximum possible value for the overlapping region
@@ -220,7 +231,7 @@ double Stitch1D::getEndOverlap(const double &intesectionMin,
   return endOverlapVal;
 }
 
-/**Gets the rebinning parameters and calculates any missing values
+/** Gets the rebinning parameters and calculates any missing values
  @param lhsWS :: The left hand side input workspace
  @param rhsWS :: The right hand side input workspace
  @param scaleRHS :: Scale the right hand side workspace
@@ -276,7 +287,7 @@ std::vector<double> Stitch1D::getRebinParams(MatrixWorkspace_sptr &lhsWS,
   return result;
 }
 
-/**Runs the Rebin Algorithm as a child and replaces special values
+/** Runs the Rebin Algorithm as a child and replaces special values
  @param input :: The input workspace
  @param params :: a vector<double> containing rebinning parameters
  @return A shared pointer to the resulting MatrixWorkspace
@@ -334,7 +345,7 @@ MatrixWorkspace_sptr Stitch1D::rebin(MatrixWorkspace_sptr &input,
   return outWS;
 }
 
-/**Runs the Integration Algorithm as a child.
+/** Runs the Integration Algorithm as a child.
  @param input :: The input workspace
  @param start :: a double defining the start of the region to integrate
  @param stop :: a double defining the end of the region to integrate
@@ -356,7 +367,7 @@ MatrixWorkspace_sptr Stitch1D::integration(MatrixWorkspace_sptr &input,
   return outWS;
 }
 
-/**Runs the WeightedMean Algorithm as a child
+/** Runs the WeightedMean Algorithm as a child
  @param inOne :: The first input workspace
  @param inTwo :: The second input workspace
  @return A shared pointer to the resulting MatrixWorkspace
@@ -371,7 +382,7 @@ MatrixWorkspace_sptr Stitch1D::weightedMean(MatrixWorkspace_sptr &inOne,
   return outWS;
 }
 
-/**Runs the ConjoinXRuns Algorithm as a child
+/** Runs the ConjoinXRuns Algorithm as a child
  @param inWS :: Input workspace
  @return A shared pointer to the resulting MatrixWorkspace
  */
@@ -390,7 +401,7 @@ MatrixWorkspace_sptr Stitch1D::conjoinXAxis(MatrixWorkspace_sptr &inOne,
   return outWS;
 }
 
-/**Runs the SortXAxis Algorithm as a child
+/** Runs the SortXAxis Algorithm as a child
  @param inWS :: Input workspace
  @return A shared pointer to the resulting MatrixWorkspace
  */
@@ -402,7 +413,7 @@ MatrixWorkspace_sptr Stitch1D::sortXAxis(MatrixWorkspace_sptr &inWS) {
   return outWS;
 }
 
-/**Runs the CreateSingleValuedWorkspace Algorithm as a child
+/** Runs the CreateSingleValuedWorkspace Algorithm as a child
  @param val :: The double to convert to a single value workspace
  @return A shared pointer to the resulting MatrixWorkspace
  */
@@ -415,7 +426,7 @@ MatrixWorkspace_sptr Stitch1D::singleValueWS(double val) {
   return outWS;
 }
 
-/**finds the bins containing the ends of the overlapping region
+/** Finds the bins containing the ends of the overlapping region
  @param startOverlap :: The start of the overlapping region
  @param endOverlap :: The end of the overlapping region
  @param workspace :: The workspace to determine the overlaps inside
@@ -435,7 +446,7 @@ Stitch1D::findStartEndIndexes(double startOverlap, double endOverlap,
   return boost::tuple<int, int>(a1, a2);
 }
 
-/**Determines if a workspace has non zero errors
+/** Determines if a workspace has non zero errors
  @param ws :: The input workspace
  @return True if there are any non-zero errors in the workspace
  */
@@ -499,7 +510,7 @@ void Stitch1D::exec() {
 
   if (startOverlap < xMin) {
     std::string message = boost::str(
-        boost::format("Stitch1D StartOverlap is outside the available X range "
+        boost::format("Stitch1D StartOverlap is outside the available X range. "
                       "after rebinning. StartOverlap: %10.9f, X min: %10.9f") %
         startOverlap % xMin);
     throw std::runtime_error(message);
@@ -509,7 +520,6 @@ void Stitch1D::exec() {
         boost::format("Stitch1D EndOverlap is outside the available X range "
                       "after rebinning. EndOverlap: %10.9f, X max: %10.9f") %
         endOverlap % xMax);
-
     throw std::runtime_error(message);
   }
 
@@ -526,7 +536,7 @@ void Stitch1D::exec() {
   MatrixWorkspace_sptr lhs, rhs;
 
   // If the input workspaces are histograms ...
-  if (rhsWS->isHistogramData() && lhsWS->isHistogramData()) {
+  if (lhsWS->isHistogramData()) {
     lhs = rebin(lhsWS, params);
     rhs = rebin(rhsWS, params);
   }
@@ -580,7 +590,7 @@ void Stitch1D::exec() {
   MatrixWorkspace_sptr overlapave;
 
   // If the input workspaces are histograms ...
-  if (rhsWS->isHistogramData() && lhsWS->isHistogramData()) {
+  if (lhsWS->isHistogramData()) {
     if (hasNonzeroErrors(overlap1) && hasNonzeroErrors(overlap2)) {
       overlapave = weightedMean(overlap1, overlap2);
     } else {
@@ -589,17 +599,9 @@ void Stitch1D::exec() {
       MatrixWorkspace_sptr denominator = singleValueWS(2.0);
       overlapave = sum / denominator;
     }
-  } else {
-    // If the input workspaces are point data ...
-    if (!rhsWS->isHistogramData() && !lhsWS->isHistogramData()) {
-      // Sort according to x values
-      auto ws = conjoinXAxis(overlap1, overlap2);
-      overlapave = sortXAxis(ws);
-    } else {
-      g_log.error("Input workspaces must be both histograms or point data");
-      throw std::invalid_argument(
-          "Input workspaces must be both histograms or point data");
-    }
+  } else { // The input workspaces are point data ... join & sort
+    auto ws = conjoinXAxis(overlap1, overlap2);
+    overlapave = sortXAxis(ws);
   }
 
   MatrixWorkspace_sptr result = lhs + overlapave + rhs;
@@ -615,8 +617,7 @@ void Stitch1D::exec() {
   setProperty("OutScaleFactor", scaleFactor);
 }
 
-/**
- * Put special values back.
+/** Put special values back.
  * @param ws : MatrixWorkspace to resinsert special values into.
  */
 void Stitch1D::reinsertSpecialValues(MatrixWorkspace_sptr ws) {
diff --git a/Framework/Algorithms/test/Stitch1DTest.h b/Framework/Algorithms/test/Stitch1DTest.h
index e7c78f32086..560b20d52c2 100644
--- a/Framework/Algorithms/test/Stitch1DTest.h
+++ b/Framework/Algorithms/test/Stitch1DTest.h
@@ -20,13 +20,13 @@
 #include <boost/make_shared.hpp>
 
 using namespace Mantid::API;
+using namespace Mantid::DataObjects;
 using namespace Mantid::Kernel;
 using Mantid::Algorithms::Stitch1D;
-using namespace Mantid::DataObjects;
-using Mantid::HistogramData::HistogramX;
-using Mantid::HistogramData::HistogramY;
 using Mantid::HistogramData::HistogramDx;
 using Mantid::HistogramData::HistogramE;
+using Mantid::HistogramData::HistogramX;
+using Mantid::HistogramData::HistogramY;
 using Mantid::HistogramData::LinearGenerator;
 
 double roundSix(double i) { return floor(i * 1000000. + 0.5) / 1000000.; }
@@ -44,8 +44,7 @@ MatrixWorkspace_sptr createWorkspace(const HistogramX &xData,
     outWS->mutableY(i) = yData;
     outWS->mutableE(i) = eData;
     outWS->mutableX(i) = xData;
-    outWS->setPointStandardDeviations(i, yData.size());
-    outWS->mutableDx(i) = Dx;
+    outWS->setPointStandardDeviations(i, Dx);
   }
 
   outWS->getAxis(0)->unit() = UnitFactory::Instance().create("Wavelength");
@@ -240,22 +239,49 @@ public:
                       std::runtime_error &);
   }
 
-  void test_lhsworkspace_must_be_histogram() {
-    auto lhs_ws = make_arbitrary_point_ws();
-    auto rhs_ws = make_arbitrary_histogram_ws();
-    TSM_ASSERT_THROWS(
-        "Both ws must be either histogram or point data",
-        do_stitch1D(lhs_ws, rhs_ws, -1., 1., std::vector<double>(1., 0.2)),
-        std::invalid_argument &);
+  void test_point_workspaces_pass() {
+    auto point_ws = make_arbitrary_point_ws();
+    const auto &x = HistogramX(3, LinearGenerator(-.5, 0.2));
+    const auto &y = HistogramY(3, LinearGenerator(1., 1.0));
+    const auto &e = HistogramE(3, 1.);
+    const auto &dx = HistogramDx(3, LinearGenerator(-3., 0.1));
+    auto point_ws_2 = createWorkspace(x, y, e, dx);
+    TSM_ASSERT_THROWS_NOTHING("Point workspaces should pass",
+                              do_stitch1D(point_ws, point_ws_2));
   }
 
-  void test_rhsworkspace_must_be_histogram() {
-    auto lhs_ws = make_arbitrary_histogram_ws();
-    auto rhs_ws = make_arbitrary_point_ws();
-    TSM_ASSERT_THROWS(
-        "Both ws must be either histogram or point data",
-        do_stitch1D(lhs_ws, rhs_ws, -1., 1., std::vector<double>(1., 0.2)),
-        std::invalid_argument &);
+  void test_histogram_workspaces_pass() {
+    auto histo_ws = make_arbitrary_histogram_ws();
+    const auto &x = HistogramX(3, LinearGenerator(-1.2, 0.2));
+    const auto &y = HistogramY(2, LinearGenerator(1., 1.0));
+    const auto &e = HistogramE(2, 1.);
+    const auto &dx = HistogramDx(2, LinearGenerator(-3., 0.1));
+    auto histo_ws_2 = createWorkspace(x, y, e, dx);
+    TSM_ASSERT_THROWS_NOTHING("Histogram workspaces should pass",
+                              do_stitch1D(histo_ws_2, histo_ws));
+  }
+
+  void test_input_validation() {
+    Stitch1D alg;
+    alg.setChild(true);
+    alg.setRethrows(true);
+    alg.initialize();
+    alg.setProperty("LHSWorkspace", make_arbitrary_point_ws());
+    alg.setProperty("RHSWorkspace", make_arbitrary_histogram_ws());
+    alg.setProperty("StartOverlap", -1);
+    alg.setProperty("EndOverlap", 1);
+    alg.setProperty("Params", std::vector<double>(1., 0.2));
+    alg.setProperty("ScaleRHSWorkspace", true);
+    alg.setPropertyValue("OutputWorkspace", "dummy_value");
+    alg.execute();
+    TSM_ASSERT("RHSWorkspace must be point data.",
+               !alg.isExecuted());
+
+    alg.setProperty("LHSWorkspace", make_arbitrary_histogram_ws());
+    alg.setProperty("RHSWorkspace", make_arbitrary_point_ws());
+    alg.execute();
+    TSM_ASSERT("RHSWorkspace must be a histogram.",
+               !alg.isExecuted());
   }
 
   void test_stitching_uses_supplied_params() {
@@ -549,7 +575,6 @@ public:
     TSM_ASSERT("All error values are non-zero", alg.hasNonzeroErrors(ws));
 
     // Run it again with all zeros
-
     e = HistogramE(9, 0.);
     ws = createWorkspace(x, y, e, dx, nspectrum);
     TSM_ASSERT("All error values are non-zero", !alg.hasNonzeroErrors(ws));
-- 
GitLab