From 0d27534028a62072573c1b97fa76565b17a604c7 Mon Sep 17 00:00:00 2001
From: Samuel Jackson <samueljackson@outlook.com>
Date: Wed, 6 Jun 2018 20:11:28 +0100
Subject: [PATCH] Add a validator for MD frames.

Add a MD workspace validator for checking that a MDWorkspace conforms to
a particular frame (e.g. HKL, QLab, QSample...)
---
 Framework/API/CMakeLists.txt                  |   3 +
 Framework/API/inc/MantidAPI/IMDWorkspace.h    |   1 +
 .../API/inc/MantidAPI/MDFrameValidator.h      |  55 +++
 Framework/API/src/MDFrameValidator.cpp        |  43 +++
 Framework/API/test/MDFrameValidatorTest.h     |  69 ++++
 Framework/Kernel/CMakeLists.txt               |   1 -
 .../inc/MantidTestHelpers/FakeObjects.h       | 321 +++++++++++++++++-
 7 files changed, 491 insertions(+), 2 deletions(-)
 create mode 100644 Framework/API/inc/MantidAPI/MDFrameValidator.h
 create mode 100644 Framework/API/src/MDFrameValidator.cpp
 create mode 100644 Framework/API/test/MDFrameValidatorTest.h

diff --git a/Framework/API/CMakeLists.txt b/Framework/API/CMakeLists.txt
index 5afd011c1e9..3fa7bdf8ff6 100644
--- a/Framework/API/CMakeLists.txt
+++ b/Framework/API/CMakeLists.txt
@@ -96,6 +96,7 @@ set ( SRC_FILES
 	src/LogManager.cpp
 	src/LogarithmScale.cpp
 	src/MDGeometry.cpp
+        src/MDFrameValidator.cpp
 	src/MatrixWorkspace.cpp
 	src/MatrixWorkspaceMDIterator.cpp
 	src/ModeratorModel.cpp
@@ -292,6 +293,7 @@ set ( INC_FILES
 	inc/MantidAPI/LogManager.h
 	inc/MantidAPI/LogarithmScale.h
 	inc/MantidAPI/MDGeometry.h
+        inc/MantidAPI/MDFrameValidator.h
 	inc/MantidAPI/MatrixWorkspace.h
 	inc/MantidAPI/MatrixWorkspaceMDIterator.h
 	inc/MantidAPI/MatrixWorkspaceValidator.h
@@ -420,6 +422,7 @@ set ( TEST_FILES
 	LogFilterGeneratorTest.h
 	LogManagerTest.h
 	MDGeometryTest.h
+	MDFrameValidatorTest.h
 	MatrixWorkspaceMDIteratorTest.h
 	ModeratorModelTest.h
 	MuParserUtilsTest.h
diff --git a/Framework/API/inc/MantidAPI/IMDWorkspace.h b/Framework/API/inc/MantidAPI/IMDWorkspace.h
index 8e6069d642a..5c98d9b2206 100644
--- a/Framework/API/inc/MantidAPI/IMDWorkspace.h
+++ b/Framework/API/inc/MantidAPI/IMDWorkspace.h
@@ -169,6 +169,7 @@ protected:
 
   const std::string toString() const override;
 
+
 private:
   std::string m_convention;
   IMDWorkspace *doClone() const override = 0;
diff --git a/Framework/API/inc/MantidAPI/MDFrameValidator.h b/Framework/API/inc/MantidAPI/MDFrameValidator.h
new file mode 100644
index 00000000000..7dd7da33249
--- /dev/null
+++ b/Framework/API/inc/MantidAPI/MDFrameValidator.h
@@ -0,0 +1,55 @@
+#ifndef MANTID_API_MDFRAMEVALIDATOR_H
+#define MANTID_API_MDFRAMEVALIDATOR_H
+
+#include "MantidKernel/TypedValidator.h"
+#include "MantidAPI/DllConfig.h"
+#include "MantidAPI/IMDWorkspace.h"
+
+
+/**
+  A validator which checks that the frame of the MDWorkspace referred to
+  by a WorkspaceProperty is the expected one.
+
+  Copyright &copy; 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
+  National Laboratory & European Spallation Source
+
+  This file is part of Mantid.
+
+  Mantid is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  Mantid is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+  File change history is stored at: <https://github.com/mantidproject/mantid>
+  Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+namespace Mantid {
+namespace API {
+class MANTID_API_DLL MDFrameValidator : public Kernel::TypedValidator<IMDWorkspace_sptr> {
+public:
+    explicit MDFrameValidator(const std::string& frameName);
+    /// Gets the type of the validator
+    inline std::string getType() const { return "mdframe"; }
+    /// Clone the current state
+    Kernel::IValidator_sptr clone() const override;
+
+private:
+    /// Check for validity.
+    virtual std::string checkValidity(const IMDWorkspace_sptr &workspace) const override;
+
+    /// The name of the required frame
+    const std::string m_frameID;
+};
+
+} // namespace Kernel
+} // namespace Mantid
+
+#endif // MANTID_API_MDFRAMEVALIDATOR_H
diff --git a/Framework/API/src/MDFrameValidator.cpp b/Framework/API/src/MDFrameValidator.cpp
new file mode 100644
index 00000000000..b5d29874690
--- /dev/null
+++ b/Framework/API/src/MDFrameValidator.cpp
@@ -0,0 +1,43 @@
+#include "MantidAPI/MDFrameValidator.h"
+#include "MantidKernel/IValidator.h"
+#include <boost/make_shared.hpp>
+
+using Mantid::Kernel::IValidator_sptr;
+
+namespace Mantid {
+namespace API {
+
+
+/** Constructor
+  *
+  * @param frameName :: The name of the frame that the workspace must have.
+  */
+MDFrameValidator::MDFrameValidator(const std::string &frameName)
+    : m_frameID{frameName} {}
+
+/**
+ * Clone the current state
+ */
+Kernel::IValidator_sptr MDFrameValidator::clone() const {
+  return boost::make_shared<MDFrameValidator>(*this);
+}
+
+/** Checks that the frame of the MDWorkspace matches the expected frame.
+  *
+  * @param value :: The workspace to test
+  * @return A user level description of the error or "" for no error
+  */
+std::string
+MDFrameValidator::checkValidity(const IMDWorkspace_sptr &workspace) const {
+
+  for (size_t index = 0; index < workspace->getNumDims(); ++index) {
+    const auto& frame = workspace->getDimension(index)->getMDFrame();
+    if (frame.name() != m_frameID)
+      return "MDWorkspace must be in the " + m_frameID + " frame.";
+  }
+
+  return "";
+}
+
+} // namespace API
+} // namespace Mantid
\ No newline at end of file
diff --git a/Framework/API/test/MDFrameValidatorTest.h b/Framework/API/test/MDFrameValidatorTest.h
new file mode 100644
index 00000000000..9a6009665af
--- /dev/null
+++ b/Framework/API/test/MDFrameValidatorTest.h
@@ -0,0 +1,69 @@
+#ifndef MANTID_MDUNITVALIDATOR_TEST_H
+#define MANTID_MDUNITVALIDATOR_TEST_H
+
+#include <boost/make_shared.hpp>
+#include <cxxtest/TestSuite.h>
+
+#include "MantidAPI/IMDEventWorkspace_fwd.h"
+#include "MantidAPI/MDFrameValidator.h"
+#include "MantidGeometry/MDGeometry/HKL.h"
+#include "MantidGeometry/MDGeometry/MDFrameFactory.h"
+#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
+#include "MantidGeometry/MDGeometry/QLab.h"
+#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
+#include "MantidKernel/UnitLabelTypes.h"
+#include "MantidTestHelpers/FakeObjects.h"
+
+using namespace Mantid::Geometry;
+using namespace Mantid::API;
+
+class MDFrameValidatorTest : public CxxTest::TestSuite {
+public:
+  void testGetType() {
+    MDFrameValidator unitValidator(HKL::HKLName);
+    TS_ASSERT_EQUALS(unitValidator.getType(), "mdframe");
+  }
+
+  void testHKLMDWorkspaceIsValidForValidatorWithHKLFrame() {
+    MDFrameValidator frameValidator(HKL::HKLName);
+
+    HKLFrameFactory factory;
+    auto frame =
+        factory.create(MDFrameArgument{HKL::HKLName, Units::Symbol::RLU});
+    auto dim = boost::make_shared<MDHistoDimension>("x", "x", *frame, 0.0f,
+                                                    100.0f, 10);
+    auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim, dim, dim);
+    TS_ASSERT_EQUALS(frameValidator.isValid(ws), "")
+  };
+
+  void testHKLMDWorkspaceIsNotValidForValidatorWithQLabFrame() {
+    MDFrameValidator frameValidator(QLab::QLabName);
+
+    MDFrameArgument args{HKL::HKLName, Units::Symbol::RLU};
+    auto frame = HKLFrameFactory().create(args);
+    auto dim = boost::make_shared<MDHistoDimension>("x", "x", *frame, 0.0f,
+                                                    100.0f, 10);
+    auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim, dim, dim);
+    TS_ASSERT_EQUALS(frameValidator.isValid(ws),
+                     "MDWorkspace must be in the " + QLab::QLabName + " frame.")
+  };
+
+  void testMixedAxisMDWorkspaceIsNotValidForValidatorWithQLabFrame() {
+    MDFrameValidator frameValidator(QLab::QLabName);
+
+    MDFrameArgument axisArgs1 {HKL::HKLName, Units::Symbol::RLU};
+    MDFrameArgument axisArgs2 {QLab::QLabName, Units::Symbol::InverseAngstrom};
+
+    auto frame1 = HKLFrameFactory().create(axisArgs1);
+    auto frame2 = QLabFrameFactory().create(axisArgs2);
+    auto dim1 = boost::make_shared<MDHistoDimension>("x", "x", *frame1, 0.0f,
+                                                    100.0f, 10);
+    auto dim2 = boost::make_shared<MDHistoDimension>("x", "x", *frame1, 0.0f,
+                                                     100.0f, 10);
+    auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim1, dim2, dim2);
+    TS_ASSERT_EQUALS(frameValidator.isValid(ws),
+                     "MDWorkspace must be in the " + QLab::QLabName + " frame.")
+  };
+};
+
+#endif // MANTID_MDUNITVALIDATOR_TEST_H
diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt
index 878ad4bb531..78c1b6bb987 100644
--- a/Framework/Kernel/CMakeLists.txt
+++ b/Framework/Kernel/CMakeLists.txt
@@ -219,7 +219,6 @@ set ( INC_FILES
 	inc/MantidKernel/Logger.h
 	inc/MantidKernel/MDAxisValidator.h
 	inc/MantidKernel/MDUnit.h
-	inc/MantidKernel/MDUnitFactory.h
 	inc/MantidKernel/MRUList.h
 	inc/MantidKernel/MagneticFormFactorTable.h
 	inc/MantidKernel/MagneticIon.h
diff --git a/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h b/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h
index 542ec7189b3..bae677cfd53 100644
--- a/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h
+++ b/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h
@@ -22,6 +22,8 @@
 #include <map>
 #include <string>
 
+#include "MantidAPI/IMDHistoWorkspace.h"
+#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
 #include "MantidAPI/ISpectrum.h"
 #include "MantidAPI/ITableWorkspace.h"
 #include "MantidAPI/MatrixWorkspace.h"
@@ -29,12 +31,17 @@
 #include "MantidAPI/SpectraAxis.h"
 #include "MantidGeometry/Instrument.h"
 #include "MantidGeometry/Instrument/DetectorGroup.h"
+#include "MantidKernel/SpecialCoordinateSystem.h"
 #include "MantidKernel/cow_ptr.h"
 
 using namespace Mantid::API;
+using namespace Mantid::Kernel;
+using namespace Mantid::Geometry;
+using Mantid::MantidVec;
+using Mantid::coord_t;
 using Mantid::detid_t;
+using Mantid::signal_t;
 using Mantid::specnum_t;
-using Mantid::MantidVec;
 
 //===================================================================================================================
 /** Helper class that implements ISpectrum */
@@ -412,6 +419,318 @@ protected:
   }
 };
 
+//===================================================================================================================
+class MDHistoWorkspaceTester : public IMDHistoWorkspace {
+
+public:
+  virtual uint64_t getNPoints() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+  virtual uint64_t getNEvents() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual std::vector<std::unique_ptr<IMDIterator>> createIterators(
+      size_t suggestedNumCores = 1,
+      Mantid::Geometry::MDImplicitFunction *function = nullptr) const override {
+      UNUSED_ARG(suggestedNumCores)
+      UNUSED_ARG(function)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t
+  getSignalAtCoord(const coord_t *coords,
+                   const Mantid::API::MDNormalization &normalization) const override {
+      UNUSED_ARG(coords);
+      UNUSED_ARG(normalization);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalWithMaskAtCoord(
+      const coord_t *coords,
+      const Mantid::API::MDNormalization &normalization) const override {
+      UNUSED_ARG(coords);
+      UNUSED_ARG(normalization);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual void
+  setMDMasking(Mantid::Geometry::MDImplicitFunction *maskingRegion) override {
+      UNUSED_ARG(maskingRegion);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual void clearMDMasking() override { throw std::runtime_error("Not Implemented"); }
+
+  virtual SpecialCoordinateSystem getSpecialCoordinateSystem() const override{
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual coord_t getInverseVolume() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t *getSignalArray() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t *getErrorSquaredArray() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t *getNumEventsArray() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual void setTo(signal_t signal, signal_t errorSquared,
+                     signal_t numEvents) override {
+      UNUSED_ARG(signal);
+      UNUSED_ARG(errorSquared);
+      UNUSED_ARG(numEvents);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual Mantid::Kernel::VMD getCenter(size_t linearIndex) const override {
+      UNUSED_ARG(linearIndex);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual void setSignalAt(size_t index, signal_t value) override {
+      UNUSED_ARG(index)
+      UNUSED_ARG(value)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual void setErrorSquaredAt(size_t index, signal_t value) override {
+      UNUSED_ARG(index)
+      UNUSED_ARG(value)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorAt(size_t index) const override {
+      UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorAt(size_t index1, size_t index2) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorAt(size_t index1, size_t index2,
+                              size_t index3) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorAt(size_t index1, size_t index2, size_t index3,
+                              size_t index4) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+      UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalAt(size_t index) const override {
+      UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalAt(size_t index1, size_t index2) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalAt(size_t index1, size_t index2,
+                               size_t index3) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalAt(size_t index1, size_t index2, size_t index3,
+                               size_t index4) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+      UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalNormalizedAt(size_t index) const override {
+      UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalNormalizedAt(size_t index1,
+                                         size_t index2) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalNormalizedAt(size_t index1, size_t index2,
+                                         size_t index3) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getSignalNormalizedAt(size_t index1, size_t index2,
+                                         size_t index3,
+                                         size_t index4) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+      UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorNormalizedAt(size_t index) const override {
+      UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorNormalizedAt(size_t index1,
+                                        size_t index2) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorNormalizedAt(size_t index1, size_t index2,
+                                        size_t index3) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t getErrorNormalizedAt(size_t index1, size_t index2,
+                                        size_t index3,
+                                        size_t index4) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+      UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t &errorSquaredAt(size_t index) override {
+      UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual signal_t &signalAt(size_t index) override {
+      UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual size_t getLinearIndex(size_t index1, size_t index2) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual size_t getLinearIndex(size_t index1, size_t index2,
+                                size_t index3) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual size_t getLinearIndex(size_t index1, size_t index2, size_t index3,
+                                size_t index4) const override {
+      UNUSED_ARG(index1)
+      UNUSED_ARG(index2)
+      UNUSED_ARG(index3)
+      UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual LinePlot
+  getLineData(const Mantid::Kernel::VMD &start, const Mantid::Kernel::VMD &end,
+              Mantid::API::MDNormalization normalize) const override {
+      UNUSED_ARG(start)
+      UNUSED_ARG(end)
+      UNUSED_ARG(normalize)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual double &operator[](const size_t &index) override {
+      UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual void
+  setCoordinateSystem(const SpecialCoordinateSystem coordinateSystem) override {
+      UNUSED_ARG(coordinateSystem)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual void setDisplayNormalization(
+      const Mantid::API::MDNormalization &preferredNormalization) override {
+      UNUSED_ARG(preferredNormalization);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  // Check if this class has an oriented lattice on any sample object
+  virtual bool hasOrientedLattice() const override {
+    return MultipleExperimentInfos::hasOrientedLattice();
+  }
+
+  virtual size_t getMemorySize() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  virtual const std::string id() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+  virtual const std::string &getName() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+  virtual bool threadSafe() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+  virtual const std::string toString() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+    MDHistoWorkspaceTester(MDHistoDimension_sptr dimX,
+    MDHistoDimension_sptr dimY,
+    MDHistoDimension_sptr dimZ) {
+
+      m_dimensions.push_back(dimX);
+      m_dimensions.push_back(dimY);
+      m_dimensions.push_back(dimZ);
+
+      initGeometry(m_dimensions);
+  }
+protected:
+
+private:
+    std::vector<IMDDimension_sptr> m_dimensions;
+
+  IMDHistoWorkspace *doClone() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+  IMDHistoWorkspace *doCloneEmpty() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+};
+
 class VariableBinThrowingTester : public AxeslessWorkspaceTester {
   size_t blocksize() const override {
     if (getSpectrum(0).dataY().size() == getSpectrum(1).dataY().size())
-- 
GitLab