From 8eb00720bc915118ba8694bc20558068376f2946 Mon Sep 17 00:00:00 2001
From: Owen Arnold <owen.arnold@stfc.ac.uk>
Date: Tue, 1 Sep 2015 09:27:03 +0100
Subject: [PATCH] refs #13517. Use TransposeMD in VSI

We transpose any MDHistoWorkspace loaded from memory if needed.
---
 .../MDAlgorithms/src/TransposeMD.cpp          |   3 +-
 .../MDHWInMemoryLoadingPresenter.h            |  10 +
 .../src/MDHWInMemoryLoadingPresenter.cpp      | 349 ++++++++++--------
 .../test/MDHWInMemoryLoadingPresenterTest.h   |   2 +-
 .../docs/source/algorithms/Transpose-v1.rst   |   4 +-
 .../docs/source/algorithms/Transpose3D-v1.rst |   2 +-
 6 files changed, 222 insertions(+), 148 deletions(-)

diff --git a/Code/Mantid/Framework/MDAlgorithms/src/TransposeMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/TransposeMD.cpp
index 3402730f519..ec48630ce7e 100644
--- a/Code/Mantid/Framework/MDAlgorithms/src/TransposeMD.cpp
+++ b/Code/Mantid/Framework/MDAlgorithms/src/TransposeMD.cpp
@@ -120,6 +120,7 @@ void TransposeMD::exec() {
 
   // Make the output workspace in the right shape.
   auto outWS = MDHistoWorkspace_sptr(new MDHistoWorkspace(targetGeometry));
+  outWS->copyExperimentInfos(*inWS);
 
   // Configure the coordinate transform.
   std::vector<coord_t> scaling(nDimsOutput, 1); // No scaling
@@ -150,7 +151,7 @@ void TransposeMD::exec() {
 
       size_t index = outWS->getLinearIndexAtCoord(&outcoords[0]);
       outWS->setSignalAt(index, inIterator->getSignal());
-      const double error = inIterator->getError();
+      const signal_t error = inIterator->getError();
       outWS->setErrorSquaredAt(index, error * error);
       outWS->setNumEventsAt(index, inIterator->getNumEvents());
       outWS->setMDMaskAt(index, inIterator->getIsMasked());
diff --git a/Code/Mantid/Vates/VatesAPI/inc/MantidVatesAPI/MDHWInMemoryLoadingPresenter.h b/Code/Mantid/Vates/VatesAPI/inc/MantidVatesAPI/MDHWInMemoryLoadingPresenter.h
index 1934847aa17..c56d65686d8 100644
--- a/Code/Mantid/Vates/VatesAPI/inc/MantidVatesAPI/MDHWInMemoryLoadingPresenter.h
+++ b/Code/Mantid/Vates/VatesAPI/inc/MantidVatesAPI/MDHWInMemoryLoadingPresenter.h
@@ -3,11 +3,15 @@
 
 #include "MantidVatesAPI/MDHWLoadingPresenter.h"
 #include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 #include <vector>
 
 class vtkDataSet;
 namespace Mantid
 {
+  namespace API{
+    class IMDHistoWorkspace;
+  }
   namespace VATES
   {
     /** 
@@ -53,12 +57,18 @@ namespace Mantid
       virtual int getSpecialCoordinates();
       std::vector<int> getExtents();
     private:
+      /// Transpose a workspace to push integrated dimensions to the last
+      boost::shared_ptr<Mantid::API::IMDHistoWorkspace> transposeWs(boost::shared_ptr<Mantid::API::IMDHistoWorkspace>& histoWs);
       /// Repository for accessing workspaces. At this level, does not specify how or where from.
       boost::scoped_ptr<WorkspaceProvider> m_repository;
       /// The name of the workspace.
       const std::string m_wsName;
+      /// The type name of the workspace
       std::string m_wsTypeName;
+      /// The workspace special coordinate system
       int m_specialCoords;
+      /// Cached visual histogram workspace. Post transpose. Avoids repeating transpose.
+      boost::shared_ptr<Mantid::API::IMDHistoWorkspace> m_cachedVisualHistoWs;
     };
   }
 }
diff --git a/Code/Mantid/Vates/VatesAPI/src/MDHWInMemoryLoadingPresenter.cpp b/Code/Mantid/Vates/VatesAPI/src/MDHWInMemoryLoadingPresenter.cpp
index ca524ac4cf2..f438c62ee1b 100644
--- a/Code/Mantid/Vates/VatesAPI/src/MDHWInMemoryLoadingPresenter.cpp
+++ b/Code/Mantid/Vates/VatesAPI/src/MDHWInMemoryLoadingPresenter.cpp
@@ -1,4 +1,6 @@
 #include "MantidVatesAPI/MDHWInMemoryLoadingPresenter.h"
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/IAlgorithm.h"
 #include "MantidAPI/IMDHistoWorkspace.h"
 #include "MantidVatesAPI/MDLoadingView.h"
 #include "MantidVatesAPI/MetaDataExtractorUtils.h"
@@ -9,160 +11,219 @@
 #include <qwt_double_interval.h>
 #include <vtkUnstructuredGrid.h>
 
-namespace Mantid
-{
-  namespace VATES
-  {
+namespace Mantid {
+namespace VATES {
+
+/*
+Constructor
+@param view : MVP view
+@param repository : Object for accessing the workspaces
+@param wsName : Name of the workspace to use.
+@throw invalid_argument if the workspace name is empty
+@throw invalid_argument if the repository is null
+@throw invalid_arument if view is null
+*/
+MDHWInMemoryLoadingPresenter::MDHWInMemoryLoadingPresenter(
+    MDLoadingView *view, WorkspaceProvider *repository, std::string wsName)
+    : MDHWLoadingPresenter(view), m_repository(repository), m_wsName(wsName),
+      m_wsTypeName(""), m_specialCoords(-1) {
+  if (m_wsName.empty()) {
+    throw std::invalid_argument("The workspace name is empty.");
+  }
+  if (NULL == repository) {
+    throw std::invalid_argument("The repository is NULL");
+  }
+  if (NULL == m_view) {
+    throw std::invalid_argument("View is NULL.");
+  }
+}
 
+/*
+Indicates whether this presenter is capable of handling the type of file that is
+attempted to be loaded.
+@return false if the file cannot be read.
+*/
+bool MDHWInMemoryLoadingPresenter::canReadFile() const {
+  bool bCanReadIt = true;
+  if (!m_repository->canProvideWorkspace(m_wsName)) {
+    // The workspace does not exist.
+    bCanReadIt = false;
+  } else if (NULL ==
+             boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(
+                 m_repository->fetchWorkspace(m_wsName)).get()) {
+    // The workspace can be found, but is not an IMDHistoWorkspace.
+    bCanReadIt = false;
+  } else {
+    // The workspace is present, and is of the correct type.
+    bCanReadIt = true;
+  }
+  return bCanReadIt;
+}
+
+/**
+ * @brief MDHWInMemoryLoadingPresenter::transposeWs
+ *
+ * vtkDataSets are usually provided in 3D, trying to create these where one of those dimensions
+ * might be integrated out leads to empty datasets. To avoid this we reorder the dimensions in our workspace
+ * prior to visualisation by transposing if if needed.
+ *
+ * @param histoWs : An input workspace that may integrated dimensions anywhere.
+ * @return A workspace that can be directly rendered from. Integrated dimensions are always last.
+ */
+Mantid::API::IMDHistoWorkspace_sptr MDHWInMemoryLoadingPresenter::transposeWs(
+    Mantid::API::IMDHistoWorkspace_sptr &histoWs) {
+  using namespace Mantid::API;
+
+  if (!m_cachedVisualHistoWs) {
     /*
-    Constructor
-    @param view : MVP view
-    @param repository : Object for accessing the workspaces
-    @param wsName : Name of the workspace to use.
-    @throw invalid_argument if the workspace name is empty
-    @throw invalid_argument if the repository is null
-    @throw invalid_arument if view is null
-    */
-  MDHWInMemoryLoadingPresenter::MDHWInMemoryLoadingPresenter(MDLoadingView* view, WorkspaceProvider* repository, std::string wsName) : MDHWLoadingPresenter(view),
-     m_repository(repository), m_wsName(wsName), m_wsTypeName(""), m_specialCoords(-1)
-  {
-    if(m_wsName.empty())
-    {
-      throw std::invalid_argument("The workspace name is empty.");
-    }
-    if(NULL == repository)
-    {
-      throw std::invalid_argument("The repository is NULL");
-    }
-    if(NULL == m_view)
-    {
-      throw std::invalid_argument("View is NULL.");
-    }
-   }
-
-     /*
-    Indicates whether this presenter is capable of handling the type of file that is attempted to be loaded.
-    @return false if the file cannot be read.
-    */
-    bool MDHWInMemoryLoadingPresenter::canReadFile() const
-    {
-      bool bCanReadIt = true;
-      if(!m_repository->canProvideWorkspace(m_wsName))
-      {
-        //The workspace does not exist.
-        bCanReadIt = false;
-      }
-      else if(NULL == boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(m_repository->fetchWorkspace(m_wsName)).get())
-      {
-        //The workspace can be found, but is not an IMDHistoWorkspace.
-        bCanReadIt = false;
-      }
-      else
-      {
-        //The workspace is present, and is of the correct type.
-        bCanReadIt = true;
+     Construct dimension indexes list for transpose. We do this by forcing
+     integrated
+     dimensions to be the last in the list. All other orderings are kept.
+     */
+    std::vector<int> integratedDims;
+    std::vector<int> nonIntegratedDims;
+    for (int i = 0; i < int(histoWs->getNumDims()); ++i) {
+      auto dim = histoWs->getDimension(i);
+      if (dim->getIsIntegrated()) {
+        integratedDims.push_back(i);
+      } else {
+        nonIntegratedDims.push_back(i);
       }
-      return bCanReadIt;
     }
+    std::vector<int> orderedDims;
+    orderedDims = nonIntegratedDims;
+    orderedDims.insert(orderedDims.end(), integratedDims.begin(),
+                       integratedDims.end());
 
     /*
-    Executes the underlying algorithm to create the MVP model.
-    @param factory : visualisation factory to use.
-    @param  : Handler for GUI updates while algorithm progresses.
-    @param drawingProgressUpdate : Handler for GUI updates while vtkDataSetFactory::create occurs.
-    */
-    vtkDataSet* MDHWInMemoryLoadingPresenter::execute(vtkDataSetFactory* factory, ProgressAction&, ProgressAction& drawingProgressUpdate)
-    {
-      using namespace Mantid::API;
-      using namespace Mantid::Geometry;
-
-      Workspace_sptr ws = m_repository->fetchWorkspace(m_wsName);
-      IMDHistoWorkspace_sptr histoWs = boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(ws);
-
-      //factory->setRecursionDepth(this->m_view->getRecursionDepth());
-      vtkDataSet* visualDataSet = factory->oneStepCreate(histoWs, drawingProgressUpdate);//HACK: progressUpdate should be argument for drawing!
-      
-      /*extractMetaData needs to be re-run here because the first execution of this from ::executeLoadMetadata will not have ensured that all dimensions
-        have proper range extents set.
-      */
-
-      // Update the meta data min and max values with the values of the visual data set. This is necessary since we want the full data range of the visual 
-      // data set and not of the actual underlying data set.
-      double* range  = visualDataSet->GetScalarRange();
-      if (range)
-      {
-        this->m_metadataJsonManager->setMinValue(range[0]);
-        this->m_metadataJsonManager->setMaxValue(range[1]);
-      }
+     If there has been any reordering above, then the dimension indexes will
+     no longer be sorted. We use that to determine if we can avoid transposing the workspace.
+     */
+    if (!std::is_sorted(orderedDims.begin(), orderedDims.end())) {
+      IAlgorithm_sptr alg = AlgorithmManager::Instance().create("TransposeMD");
+      alg->setChild(true);
+      alg->initialize();
+      alg->setProperty("InputWorkspace", histoWs);
+      alg->setPropertyValue("OutputWorkspace", "dummy");
+      alg->setProperty("Axes", orderedDims);
+      alg->execute();
+      IMDHistoWorkspace_sptr visualHistoWs =
+          alg->getProperty("OutputWorkspace");
+      m_cachedVisualHistoWs = visualHistoWs;
+    } else {
+      // No need to transpose anything.
+      m_cachedVisualHistoWs = histoWs;
+    }
+  }
+  return m_cachedVisualHistoWs;
+}
 
-      this->extractMetadata(histoWs);
+/*
+Executes the underlying algorithm to create the MVP model.
+@param factory : visualisation factory to use.
+@param  : Handler for GUI updates while algorithm progresses.
+@param drawingProgressUpdate : Handler for GUI updates while
+vtkDataSetFactory::create occurs.
+*/
+vtkDataSet *
+MDHWInMemoryLoadingPresenter::execute(vtkDataSetFactory *factory,
+                                      ProgressAction &,
+                                      ProgressAction &drawingProgressUpdate) {
+  using namespace Mantid::API;
+  using namespace Mantid::Geometry;
+
+  Workspace_sptr ws = m_repository->fetchWorkspace(m_wsName);
+  IMDHistoWorkspace_sptr histoWs =
+      boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(ws);
+
+  auto visualHistoWs = transposeWs(histoWs);
+
+  // factory->setRecursionDepth(this->m_view->getRecursionDepth());
+  vtkDataSet *visualDataSet = factory->oneStepCreate(
+      visualHistoWs, drawingProgressUpdate); // HACK: progressUpdate should be
+                                             // argument for drawing!
+
+  /*extractMetaData needs to be re-run here because the first execution of this
+    from ::executeLoadMetadata will not have ensured that all dimensions
+    have proper range extents set.
+  */
+
+  // Update the meta data min and max values with the values of the visual data
+  // set. This is necessary since we want the full data range of the visual
+  // data set and not of the actual underlying data set.
+  double *range = visualDataSet->GetScalarRange();
+  if (range) {
+    this->m_metadataJsonManager->setMinValue(range[0]);
+    this->m_metadataJsonManager->setMaxValue(range[1]);
+  }
 
-      this->appendMetadata(visualDataSet, histoWs->getName());
-      return visualDataSet;
-    }
+  this->extractMetadata(visualHistoWs);
 
-    /**
-     Executes any meta-data loading required.
-    */
-    void MDHWInMemoryLoadingPresenter::executeLoadMetadata()
-    {
-      using namespace Mantid::API;
-
-      Workspace_sptr ws = m_repository->fetchWorkspace(m_wsName);
-      IMDHistoWorkspace_sptr histoWs = boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(ws);
-      m_wsTypeName = histoWs->id();
-      m_specialCoords = histoWs->getSpecialCoordinateSystem();
-      
-      // Set the minimum and maximum of the workspace data.
-      QwtDoubleInterval minMaxContainer =  m_metaDataExtractor->getMinAndMax(histoWs);
-      m_metadataJsonManager->setMinValue(minMaxContainer.minValue());
-      m_metadataJsonManager->setMaxValue(minMaxContainer.maxValue());
-
-      // Set the instrument which is associated with the workspace.
-      m_metadataJsonManager->setInstrument(m_metaDataExtractor->extractInstrument(histoWs));
-
-      // Set the special coordinates
-      m_metadataJsonManager->setSpecialCoordinates(m_specialCoords);
-
-      //Call base-class extraction method.
-      this->extractMetadata(histoWs);
-    }
+  this->appendMetadata(visualDataSet, histoWs->getName());
+  return visualDataSet;
+}
 
-    ///Destructor
-    MDHWInMemoryLoadingPresenter::~MDHWInMemoryLoadingPresenter()
-    {
-      delete m_view;
-    }
+/**
+ Executes any meta-data loading required.
+*/
+void MDHWInMemoryLoadingPresenter::executeLoadMetadata() {
+  using namespace Mantid::API;
 
-    /*
-     * Getter for the workspace type name.
-     * @return Workspace Type Name
-     */
-    std::string MDHWInMemoryLoadingPresenter::getWorkspaceTypeName()
-    {
-      return m_wsTypeName;
-    }
+  Workspace_sptr ws = m_repository->fetchWorkspace(m_wsName);
+  IMDHistoWorkspace_sptr histoWs =
+      boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(ws);
+  m_wsTypeName = histoWs->id();
+  m_specialCoords = histoWs->getSpecialCoordinateSystem();
 
-    /**
-     * Getter for the special coordinates.
-     * @return the special coordinates value
-     */
-    int MDHWInMemoryLoadingPresenter::getSpecialCoordinates()
-    {
-      return m_specialCoords;
-    }
-      
-    std::vector<int> MDHWInMemoryLoadingPresenter::getExtents()
-    {
-      using namespace Mantid::API;
-      Workspace_sptr ws = m_repository->fetchWorkspace(m_wsName);
-      IMDHistoWorkspace_sptr histoWs = boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(ws);
-      std::vector<int> extents(6, 0);
-      extents[1] = static_cast<int>(histoWs->getXDimension()->getNBins());
-      extents[3] = static_cast<int>(histoWs->getYDimension()->getNBins());
-      extents[5] = static_cast<int>(histoWs->getZDimension()->getNBins());
-      return extents;
-    }
-      
-  }
+  histoWs = transposeWs(histoWs);
+
+  // Set the minimum and maximum of the workspace data.
+  QwtDoubleInterval minMaxContainer =
+      m_metaDataExtractor->getMinAndMax(histoWs);
+  m_metadataJsonManager->setMinValue(minMaxContainer.minValue());
+  m_metadataJsonManager->setMaxValue(minMaxContainer.maxValue());
+
+  // Set the instrument which is associated with the workspace.
+  m_metadataJsonManager->setInstrument(
+      m_metaDataExtractor->extractInstrument(histoWs));
+
+  // Set the special coordinates
+  m_metadataJsonManager->setSpecialCoordinates(m_specialCoords);
+
+  // Call base-class extraction method.
+  this->extractMetadata(histoWs);
+}
+
+/// Destructor
+MDHWInMemoryLoadingPresenter::~MDHWInMemoryLoadingPresenter() { delete m_view; }
+
+/*
+ * Getter for the workspace type name.
+ * @return Workspace Type Name
+ */
+std::string MDHWInMemoryLoadingPresenter::getWorkspaceTypeName() {
+  return m_wsTypeName;
+}
+
+/**
+ * Getter for the special coordinates.
+ * @return the special coordinates value
+ */
+int MDHWInMemoryLoadingPresenter::getSpecialCoordinates() {
+  return m_specialCoords;
+}
+
+std::vector<int> MDHWInMemoryLoadingPresenter::getExtents() {
+  using namespace Mantid::API;
+  Workspace_sptr ws = m_repository->fetchWorkspace(m_wsName);
+  IMDHistoWorkspace_sptr histoWs =
+      boost::dynamic_pointer_cast<Mantid::API::IMDHistoWorkspace>(ws);
+  histoWs = transposeWs(histoWs);
+  std::vector<int> extents(6, 0);
+  extents[1] = static_cast<int>(histoWs->getXDimension()->getNBins());
+  extents[3] = static_cast<int>(histoWs->getYDimension()->getNBins());
+  extents[5] = static_cast<int>(histoWs->getZDimension()->getNBins());
+  return extents;
+}
+}
 }
diff --git a/Code/Mantid/Vates/VatesAPI/test/MDHWInMemoryLoadingPresenterTest.h b/Code/Mantid/Vates/VatesAPI/test/MDHWInMemoryLoadingPresenterTest.h
index 1b61f8ac85a..461b007f8f7 100644
--- a/Code/Mantid/Vates/VatesAPI/test/MDHWInMemoryLoadingPresenterTest.h
+++ b/Code/Mantid/Vates/VatesAPI/test/MDHWInMemoryLoadingPresenterTest.h
@@ -32,7 +32,7 @@ private:
   };
 
   // Helper method. Generates and returns a valid IMDHistoWorkspace
-  static Mantid::API::Workspace_sptr getGoodWorkspace()
+  Mantid::API::Workspace_sptr getGoodWorkspace()
   {
     Mantid::DataObjects::MDHistoWorkspace_sptr ws = makeFakeMDHistoWorkspace(1.0, 4, 5, 1.0, 0.1,"MD_HISTO_WS");
     return ws;
diff --git a/Code/Mantid/docs/source/algorithms/Transpose-v1.rst b/Code/Mantid/docs/source/algorithms/Transpose-v1.rst
index 44218c1a7f5..15f8b725124 100644
--- a/Code/Mantid/docs/source/algorithms/Transpose-v1.rst
+++ b/Code/Mantid/docs/source/algorithms/Transpose-v1.rst
@@ -10,7 +10,7 @@ Description
 -----------
 
 This algorithm transposes a workspace, so that an N1 x N2 workspace
-becomes N2 x N1.
+becomes N2 x N1. 
 
 The X-vector values for the new workspace are taken from the axis values
 of the old workspace, which is generaly the spectra number but can be
@@ -22,6 +22,8 @@ other values, if say the workspace has gone through
     The new axis values are taken from the previous X-vector values for the
     first specrum in the workspace. For this reason, use with ragged
     workspaces is undefined.
+    
+For transposing multidimensional workspaces use :ref:`TransposeMD <algm-TransposeMD>`.
 
 Usage
 -----
diff --git a/Code/Mantid/docs/source/algorithms/Transpose3D-v1.rst b/Code/Mantid/docs/source/algorithms/Transpose3D-v1.rst
index 0b31bfbfb50..ddb63add8e1 100644
--- a/Code/Mantid/docs/source/algorithms/Transpose3D-v1.rst
+++ b/Code/Mantid/docs/source/algorithms/Transpose3D-v1.rst
@@ -8,7 +8,7 @@
 
 .. warning::
 
-    This algorithm is currently under review and may change or disappear in a future release of Mantid.
+    For transposing multidimensional workspaces now use :ref:`TransposeMD <algm-TransposeMD>`. This algorithm is currently under review and may change or disappear in a future release of Mantid. 
 
 
 
-- 
GitLab