From 7aa491f8c2191b003699a6d3e11a91cb2f074523 Mon Sep 17 00:00:00 2001
From: Janik Zikovsky <zikovskyjl@ornl.gov>
Date: Mon, 13 Feb 2012 17:27:40 -0500
Subject: [PATCH] Refs #4794: addEventUnsafe() method on MDBoxes

To try to speed up MergeMDFiles, however my second try is 1172 seconds which is slightly slower than before.
---
 .../inc/MantidMDAlgorithms/MergeMDFiles.h     |  12 +-
 .../MDAlgorithms/src/MergeMDFiles.cpp         | 397 +++++++++---------
 .../MDEvents/inc/MantidMDEvents/IMDBox.h      |  17 +-
 .../MDEvents/inc/MantidMDEvents/MDBox.h       |   9 +-
 .../MDEvents/inc/MantidMDEvents/MDEvent.h     |   7 +-
 .../MDEvents/inc/MantidMDEvents/MDGridBox.h   |   4 +-
 .../MDEvents/inc/MantidMDEvents/MDLeanEvent.h |   8 +-
 Code/Mantid/Framework/MDEvents/src/IMDBox.cpp |  59 ++-
 Code/Mantid/Framework/MDEvents/src/MDBox.cpp  |  49 ++-
 .../Framework/MDEvents/src/MDGridBox.cpp      |  34 +-
 .../Framework/MDEvents/test/IMDBoxTest.h      |   4 +
 .../Framework/MDEvents/test/MDBoxTest.h       |  18 +-
 .../Framework/MDEvents/test/MDGridBoxTest.h   |   2 +-
 .../NexusCPP/inc/MantidNexusCPP/NeXusFile.hpp |   6 +
 14 files changed, 391 insertions(+), 235 deletions(-)

diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/MergeMDFiles.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/MergeMDFiles.h
index 188bbae8ebd..41a468eb756 100644
--- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/MergeMDFiles.h
+++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/MergeMDFiles.h
@@ -62,21 +62,21 @@ namespace MDAlgorithms
     template<typename MDE, size_t nd>
     void loadBoxData();
 
-    template<typename MDE, size_t nd>
-    typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr createOutputWS(typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr ws);
-
     template<typename MDE, size_t nd>
     typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr createOutputWSbyCloning(typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr ws);
 
-    template<typename MDE, size_t nd>
-    void doExec(typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr ws);
-
     template<typename MDE, size_t nd>
     void doExecByCloning(typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr ws);
 
     template<typename MDE, size_t nd>
     void finalizeOutput(typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr outWS);
 
+//    template<typename MDE, size_t nd>
+//    typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr createOutputWS(typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr ws);
+//
+//    template<typename MDE, size_t nd>
+//    void doExec(typename Mantid::MDEvents::MDEventWorkspace<MDE, nd>::sptr ws);
+
   public:
 
     /// Files to load
diff --git a/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp b/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp
index ac8b61ea92d..2add40e13ba 100644
--- a/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp
+++ b/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp
@@ -12,6 +12,8 @@ Then, enter the path to all of the files created previously. The algorithm avoid
 keeping the events from ONE box from ALL the files in memory at once to further process and refine it.
 This is why it requires a common box structure.
 
+See also: [[MergeMD]], for merging any MDWorkspaces in system memory (faster, but needs more memory).
+
 *WIKI*/
 
 #include "MantidAPI/FileProperty.h"
@@ -80,78 +82,198 @@ namespace MDAlgorithms
   }
 
 
-  //======================================================================================
-  /** Task that loads all of the events from a particular block from a file
-   * that is being merged and then adds them onto the output workspace.
-   */
-  TMDE_CLASS
-  class MergeMDLoadTask : public Mantid::Kernel::Task
-  {
-  public:
-    /** Constructor
-     *
-     * @param alg :: MergeMDFiles Algorithm - used to pass parameters etc. around
-     * @param blockNum :: Which block to load?
-     * @param outWS :: Output workspace
-     */
-    MergeMDLoadTask(MergeMDFiles * alg, size_t blockNum, typename MDEventWorkspace<MDE, nd>::sptr outWS)
-    : m_alg(alg), m_blockNum(blockNum), outWS(outWS)
-    {
-    }
-
-    //---------------------------------------------------------------------------------------------
-    /** Main method that performs the work for the task. */
-    void run()
-    {
-      // Vector of events accumulated from ALL files to merge.
-      std::vector<MDE> events;
-
-      // Go through each file
-      this->m_alg->fileMutex.lock();
-      for (size_t iw=0; iw<m_alg->files.size(); iw++)
-      {
-        // The file and the indexes into that file
-        ::NeXus::File * file = this->m_alg->files[iw];
-        std::vector<uint64_t> & box_event_index = this->m_alg->box_indexes[iw];
-
-        uint64_t indexStart = box_event_index[this->m_blockNum*2+0];
-        uint64_t numEvents = box_event_index[this->m_blockNum*2+1];
-        // This will APPEND the events to the one vector
-        MDE::loadVectorFromNexusSlab(events, file, indexStart, numEvents);
-      } // For each file
-      this->m_alg->fileMutex.unlock();
-
-      if (!events.empty())
-      {
-        // Add all the events from the same box
-        outWS->addEvents( events );
-
-        // Track the total number of added events
-        m_alg->statsMutex.lock();
-        m_alg->totalLoaded += uint64_t(events.size());
-        m_alg->getLogger().debug() << "Box " << m_blockNum << ". Total events " << m_alg->totalLoaded << ". This one added " << events.size() << ". "<< std::endl;
-        // Report the progress
-        m_alg->prog->reportIncrement(events.size(), "Loading Box");
-        m_alg->statsMutex.unlock();
-      } // there was something loaded
-
-    }
-
-
-  protected:
-    /// MergeMDFiles Algorithm - used to pass parameters etc. around
-    MergeMDFiles * m_alg;
-    /// Which block to load?
-    size_t m_blockNum;
-    /// Output workspace
-    typename MDEventWorkspace<MDE, nd>::sptr outWS;
-  };
-
-
-
-
-
 
+//
+//  //======================================================================================
+//  /** Task that loads all of the events from a particular block from a file
+//   * that is being merged and then adds them onto the output workspace.
+//   */
+//  TMDE_CLASS
+//  class MergeMDLoadTask : public Mantid::Kernel::Task
+//  {
+//  public:
+//    /** Constructor
+//     *
+//     * @param alg :: MergeMDFiles Algorithm - used to pass parameters etc. around
+//     * @param blockNum :: Which block to load?
+//     * @param outWS :: Output workspace
+//     */
+//    MergeMDLoadTask(MergeMDFiles * alg, size_t blockNum, typename MDEventWorkspace<MDE, nd>::sptr outWS)
+//    : m_alg(alg), m_blockNum(blockNum), outWS(outWS)
+//    {
+//    }
+//
+//    //---------------------------------------------------------------------------------------------
+//    /** Main method that performs the work for the task. */
+//    void run()
+//    {
+//      // Vector of events accumulated from ALL files to merge.
+//      std::vector<MDE> events;
+//
+//      // Go through each file
+//      this->m_alg->fileMutex.lock();
+//      for (size_t iw=0; iw<m_alg->files.size(); iw++)
+//      {
+//        // The file and the indexes into that file
+//        ::NeXus::File * file = this->m_alg->files[iw];
+//        std::vector<uint64_t> & box_event_index = this->m_alg->box_indexes[iw];
+//
+//        uint64_t indexStart = box_event_index[this->m_blockNum*2+0];
+//        uint64_t numEvents = box_event_index[this->m_blockNum*2+1];
+//        // This will APPEND the events to the one vector
+//        MDE::loadVectorFromNexusSlab(events, file, indexStart, numEvents);
+//      } // For each file
+//      this->m_alg->fileMutex.unlock();
+//
+//      if (!events.empty())
+//      {
+//        // Add all the events from the same box
+//        outWS->addEvents( events );
+//
+//        // Track the total number of added events
+//        m_alg->statsMutex.lock();
+//        m_alg->totalLoaded += uint64_t(events.size());
+//        m_alg->getLogger().debug() << "Box " << m_blockNum << ". Total events " << m_alg->totalLoaded << ". This one added " << events.size() << ". "<< std::endl;
+//        // Report the progress
+//        m_alg->prog->reportIncrement(events.size(), "Loading Box");
+//        m_alg->statsMutex.unlock();
+//      } // there was something loaded
+//
+//    }
+//
+//
+//  protected:
+//    /// MergeMDFiles Algorithm - used to pass parameters etc. around
+//    MergeMDFiles * m_alg;
+//    /// Which block to load?
+//    size_t m_blockNum;
+//    /// Output workspace
+//    typename MDEventWorkspace<MDE, nd>::sptr outWS;
+//  };
+//
+//
+//
+//
+//
+//  //----------------------------------------------------------------------------------------------
+//  /** Create the output workspace using the input as a guide
+//   *
+//   * @param ws :: first workspace from the inputs
+//   * @return the MDEventWorkspace sptr.
+//   */
+//  template<typename MDE, size_t nd>
+//  typename MDEventWorkspace<MDE, nd>::sptr MergeMDFiles::createOutputWS(typename MDEventWorkspace<MDE, nd>::sptr ws)
+//  {
+//    // Use the copy constructor to get the same dimensions etc.
+//    typename MDEventWorkspace<MDE, nd>::sptr outWS(new MDEventWorkspace<MDE, nd>(*ws));
+//    this->outIWS = outWS;
+//
+//    std::string outputFile = getProperty("OutputFilename");
+//
+//    // Fix the box controller settings in the output workspace so that it splits normally
+//    BoxController_sptr bc = outWS->getBoxController();
+//    // TODO: Specify these split parameters some smarter way?
+//    bc->setMaxDepth(20);
+//    bc->setSplitInto(4);
+//    bc->setSplitThreshold(10000);
+//
+//    // Perform the initial box splitting.
+//    IMDBox<MDE,nd> * box = outWS->getBox();
+//    for (size_t d=0; d<nd; d++)
+//      box->setExtents(d, outWS->getDimension(d)->getMinimum(), outWS->getDimension(d)->getMaximum());
+//    box->setBoxController(bc);
+//    outWS->splitBox();
+//
+//    // Save the empty WS and turn it into a file-backed MDEventWorkspace
+//    if (!outputFile.empty())
+//    {
+//      IAlgorithm_sptr saver = this->createSubAlgorithm("SaveMD" ,0.01, 0.05);
+//      saver->setProperty("InputWorkspace", outIWS);
+//      saver->setPropertyValue("Filename", outputFile);
+//      saver->setProperty("MakeFileBacked", true);
+//      saver->executeAsSubAlg();
+//    }
+//
+//    // Complete the file-back-end creation.
+//    DiskBuffer & dbuf = bc->getDiskBuffer(); UNUSED_ARG(dbuf);
+//    g_log.notice() << "Setting cache to 400 MB write." << std::endl;
+//    bc->setCacheParameters(sizeof(MDE), 400000000/sizeof(MDE));
+//
+//
+//    return outWS;
+//  }
+//
+//
+//  //----------------------------------------------------------------------------------------------
+//  /** Perform the merging, with generalized output workspace
+//   *
+//   * @param ws :: first MDEventWorkspace in the list to merge
+//   */
+//  template<typename MDE, size_t nd>
+//  void MergeMDFiles::doExec(typename MDEventWorkspace<MDE, nd>::sptr ws)
+//  {
+//    // First, load all the box data
+//    this->loadBoxData<MDE,nd>();
+//
+//    // Now create the output workspace
+//    typename MDEventWorkspace<MDE, nd>::sptr outWS = this->createOutputWS<MDE,nd>(ws);
+//
+//    // Progress report based on events processed.
+//    this->prog = new Progress(this, 0.1, 0.9, size_t(totalEvents));
+//    this->prog->setNotifyStep(0.1);
+//
+//    // For tracking progress
+//    uint64_t totalEventsInTasks = 0;
+//    this->totalLoaded = 0;
+//
+//    // Prepare thread pool
+//    CPUTimer overallTime;
+//    ThreadSchedulerFIFO * ts = new ThreadSchedulerFIFO();
+//    ThreadPool tp(ts);
+//
+//    for (size_t ib=0; ib<numBoxes; ib++)
+//    {
+//      // Add a task for each box that actually has some events
+//      if (this->eventsPerBox[ib] > 0)
+//      {
+//        totalEventsInTasks += eventsPerBox[ib];
+//        MergeMDLoadTask<MDE,nd> * task = new MergeMDLoadTask<MDE,nd>(this, ib, outWS);
+//        ts->push(task);
+//      }
+//
+//      // You've added enough tasks that will fill up some memory.
+//      if (totalEventsInTasks > 10000000)
+//      {
+//        // Run all the tasks
+//        tp.joinAll();
+//
+//        // Occasionally release free memory (has an effect on Linux only).
+//        MemoryManager::Instance().releaseFreeMemory();
+//
+//        // Now do all the splitting tasks
+//        g_log.information() << "Splitting boxes since we have added " << totalEventsInTasks << " events." << std::endl;
+//        outWS->splitAllIfNeeded(ts);
+//        if (ts->size() > 0)
+//          prog->doReport("Splitting Boxes");
+//        tp.joinAll();
+//
+//        totalEventsInTasks = 0;
+//      }
+//    } // for each box
+//
+//    // Run any final tasks
+//    tp.joinAll();
+//
+//    // Final splitting
+//    g_log.debug() << "Final splitting of boxes. " << totalEventsInTasks << " events." << std::endl;
+//    outWS->splitAllIfNeeded(ts);
+//    tp.joinAll();
+//    g_log.information() << overallTime << " to do all the adding." << std::endl;
+//
+//    // Finish things up
+//    this->finalizeOutput<MDE,nd>(outWS);
+//  }
+//
 
 
 
@@ -231,137 +353,6 @@ namespace MDAlgorithms
 
 
 
-  //----------------------------------------------------------------------------------------------
-  /** Create the output workspace using the input as a guide
-   *
-   * @param ws :: first workspace from the inputs
-   * @return the MDEventWorkspace sptr.
-   */
-  template<typename MDE, size_t nd>
-  typename MDEventWorkspace<MDE, nd>::sptr MergeMDFiles::createOutputWS(typename MDEventWorkspace<MDE, nd>::sptr ws)
-  {
-    // Use the copy constructor to get the same dimensions etc.
-    typename MDEventWorkspace<MDE, nd>::sptr outWS(new MDEventWorkspace<MDE, nd>(*ws));
-    this->outIWS = outWS;
-
-    std::string outputFile = getProperty("OutputFilename");
-
-    // Fix the box controller settings in the output workspace so that it splits normally
-    BoxController_sptr bc = outWS->getBoxController();
-    // TODO: Specify these split parameters some smarter way?
-    bc->setMaxDepth(20);
-    bc->setSplitInto(4);
-    bc->setSplitThreshold(10000);
-
-    // Perform the initial box splitting.
-    IMDBox<MDE,nd> * box = outWS->getBox();
-    for (size_t d=0; d<nd; d++)
-      box->setExtents(d, outWS->getDimension(d)->getMinimum(), outWS->getDimension(d)->getMaximum());
-    box->setBoxController(bc);
-    outWS->splitBox();
-
-    // Save the empty WS and turn it into a file-backed MDEventWorkspace
-    if (!outputFile.empty())
-    {
-      IAlgorithm_sptr saver = this->createSubAlgorithm("SaveMD" ,0.01, 0.05);
-      saver->setProperty("InputWorkspace", outIWS);
-      saver->setPropertyValue("Filename", outputFile);
-      saver->setProperty("MakeFileBacked", true);
-      saver->executeAsSubAlg();
-    }
-
-    // Complete the file-back-end creation.
-    DiskBuffer & dbuf = bc->getDiskBuffer(); UNUSED_ARG(dbuf);
-    g_log.notice() << "Setting cache to 400 MB write." << std::endl;
-    bc->setCacheParameters(sizeof(MDE), 400000000/sizeof(MDE));
-
-
-    return outWS;
-  }
-
-
-  //----------------------------------------------------------------------------------------------
-  /** Perform the merging, with generalized output workspace
-   *
-   * @param ws :: first MDEventWorkspace in the list to merge
-   */
-  template<typename MDE, size_t nd>
-  void MergeMDFiles::doExec(typename MDEventWorkspace<MDE, nd>::sptr ws)
-  {
-    // First, load all the box data
-    this->loadBoxData<MDE,nd>();
-
-    // Now create the output workspace
-    typename MDEventWorkspace<MDE, nd>::sptr outWS = this->createOutputWS<MDE,nd>(ws);
-
-    // Progress report based on events processed.
-    this->prog = new Progress(this, 0.1, 0.9, size_t(totalEvents));
-    this->prog->setNotifyStep(0.1);
-
-    // For tracking progress
-    uint64_t totalEventsInTasks = 0;
-    this->totalLoaded = 0;
-
-    // Prepare thread pool
-    CPUTimer overallTime;
-    ThreadSchedulerFIFO * ts = new ThreadSchedulerFIFO();
-    ThreadPool tp(ts);
-
-    for (size_t ib=0; ib<numBoxes; ib++)
-    {
-      // Add a task for each box that actually has some events
-      if (this->eventsPerBox[ib] > 0)
-      {
-        totalEventsInTasks += eventsPerBox[ib];
-        MergeMDLoadTask<MDE,nd> * task = new MergeMDLoadTask<MDE,nd>(this, ib, outWS);
-        ts->push(task);
-      }
-
-      // You've added enough tasks that will fill up some memory.
-      if (totalEventsInTasks > 10000000)
-      {
-        // Run all the tasks
-        tp.joinAll();
-
-        // Occasionally release free memory (has an effect on Linux only).
-        MemoryManager::Instance().releaseFreeMemory();
-
-        // Now do all the splitting tasks
-        g_log.information() << "Splitting boxes since we have added " << totalEventsInTasks << " events." << std::endl;
-        outWS->splitAllIfNeeded(ts);
-        if (ts->size() > 0)
-          prog->doReport("Splitting Boxes");
-        tp.joinAll();
-
-        totalEventsInTasks = 0;
-      }
-    } // for each box
-
-    // Run any final tasks
-    tp.joinAll();
-
-    // Final splitting
-    g_log.debug() << "Final splitting of boxes. " << totalEventsInTasks << " events." << std::endl;
-    outWS->splitAllIfNeeded(ts);
-    tp.joinAll();
-    g_log.information() << overallTime << " to do all the adding." << std::endl;
-
-    // Finish things up
-    this->finalizeOutput<MDE,nd>(outWS);
-  }
-
-
-
-
-
-
-
-
-
-
-
-
-
   //----------------------------------------------------------------------------------------------
   /** Create the output workspace by cloning the first one
    *
@@ -501,7 +492,7 @@ namespace MDAlgorithms
       if (!events.empty())
       {
         // Add all the events from the same box
-        outBox->addEvents( events );
+        outBox->addEventsUnsafe( events );
         events.clear();
         std::vector<MDE>().swap(events); // really free the data
 
diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/IMDBox.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/IMDBox.h
index 51d6071f973..2fcd5f4f2c8 100644
--- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/IMDBox.h
+++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/IMDBox.h
@@ -174,17 +174,16 @@ namespace MDEvents
     /// Add a single event
     virtual void addEvent(const MDE & point) = 0;
 
-    /** Add several events from a vector
-     * @param events :: vector of MDEvents to add (all of it)
-     * @return the number of events that were rejected (because of being out of bounds)
-     */
-    virtual size_t addEvents(const std::vector<MDE> & events)
-    {
-      return addEvents(events, 0, events.size());
-    }
+    /// Add a single event, with no mutex locking
+    virtual void addEventUnsafe(const MDE & point) = 0;
 
     /// Add several events, within a given range
-    virtual size_t addEvents(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at);
+    virtual size_t addEventsPart(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at);
+    size_t addEvents(const std::vector<MDE> & events);
+
+    /// Add several events, within a given range, with no bounds checking
+    virtual size_t addEventsPartUnsafe(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at);
+    size_t addEventsUnsafe(const std::vector<MDE> & events);
 
     /** Perform centerpoint binning of events
      * @param bin :: MDBin object giving the limits of events to accept.
diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h
index ba1ff1b5706..7e93ff9fd50 100644
--- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h
+++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h
@@ -162,12 +162,11 @@ namespace MDEvents
 
     void addEvent(const MDE & point);
 
-    size_t addEvents(const std::vector<MDE> & events)
-    {
-      return this->addEvents(events, 0, events.size());
-    }
+    void addEventUnsafe(const MDE & point);
 
-    size_t addEvents(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at);
+    size_t addEventsPart(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at);
+
+    size_t addEventsPartUnsafe(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at);
 
     void centerpointBin(MDBin<MDE,nd> & bin, bool * fullyContained) const;
 
diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDEvent.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDEvent.h
index 28c462530df..514b3e77f4a 100644
--- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDEvent.h
+++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDEvent.h
@@ -310,7 +310,12 @@ namespace MDEvents
       coord_t * data = new coord_t[dataSize];
 
 #ifdef COORDT_IS_FLOAT
-      if (file->getInfo().type == ::NeXus::FLOAT64)
+      // C-style call is much faster than the C++ call.
+      int dims[NX_MAXRANK];
+      int type = ::NeXus::FLOAT32;
+      int rank = 0;
+      NXgetinfo(file->getHandle(), &rank, dims, &type);
+      if (type == ::NeXus::FLOAT64)
       {
         // Handle old files that are recorded in DOUBLEs to load as FLOATS
         double * dblData = new double[dataSize];
diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h
index 46d885d6e5c..2a9af713b78 100644
--- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h
+++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h
@@ -78,6 +78,8 @@ namespace MDEvents
 
     void addEvent(const MDE & point);
 
+    void addEventUnsafe(const MDE & point);
+
     void centerpointBin(MDBin<MDE,nd> & bin, bool * fullyContained) const;
 
     void generalBin(MDBin<MDE,nd> & /*bin*/, Mantid::Geometry::MDImplicitFunction & /*function*/) const {};
@@ -192,7 +194,7 @@ namespace MDEvents
       /// Add the events in the MDGridBox.
       void run()
       {
-        box->addEvents(events, start_at, stop_at);
+        box->addEventsPart(events, start_at, stop_at);
         if (prog)
         {
           std::ostringstream out;
diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDLeanEvent.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDLeanEvent.h
index 25c8f5276ce..f967ef0daa0 100644
--- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDLeanEvent.h
+++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDLeanEvent.h
@@ -7,6 +7,7 @@
 #include "MantidGeometry/MDGeometry/MDTypes.h"
 #include <numeric>
 #include <cmath>
+#include <napi.h>
 
 namespace Mantid
 {
@@ -411,7 +412,12 @@ namespace MDEvents
       coord_t * data = new coord_t[dataSize];
 
 #ifdef COORDT_IS_FLOAT
-      if (file->getInfo().type == ::NeXus::FLOAT64)
+      // C-style call is much faster than the C++ call.
+      int dims[NX_MAXRANK];
+      int type = ::NeXus::FLOAT32;
+      int rank = 0;
+      NXgetinfo(file->getHandle(), &rank, dims, &type);
+      if (type == ::NeXus::FLOAT64)
       {
         // Handle old files that are recorded in DOUBLEs to load as FLOATS
         double * dblData = new double[dataSize];
diff --git a/Code/Mantid/Framework/MDEvents/src/IMDBox.cpp b/Code/Mantid/Framework/MDEvents/src/IMDBox.cpp
index ccc7332c7ba..078aa75d9c5 100644
--- a/Code/Mantid/Framework/MDEvents/src/IMDBox.cpp
+++ b/Code/Mantid/Framework/MDEvents/src/IMDBox.cpp
@@ -93,7 +93,7 @@ namespace MDEvents
    * @return the number of events that were rejected (because of being out of bounds)
    */
   TMDE(
-  size_t IMDBox)::addEvents(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at)
+  size_t IMDBox)::addEventsPart(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at)
   {
     size_t numBad = 0;
     // --- Go event by event and add them ----
@@ -124,6 +124,63 @@ namespace MDEvents
     return numBad;
   }
 
+  //-----------------------------------------------------------------------------------------------
+  /** Add several events, starting and stopping at particular point in a vector.
+   * This is the fastest way to add many events because:
+   *  - Bounds checking is NOT performed.
+   *  - This call is NOT thread-safe (no locking is made while adding).
+   *
+   * NOTE: You must call refreshCache() after you are done, to calculate the
+   *  nPoints, signal and error.
+   *
+   * @param events :: vector of events to be copied.
+   * @param start_at :: begin at this index in the array
+   * @param stop_at :: stop at this index in the array
+   * @return 0 (since no events were rejected)
+   */
+  TMDE(
+  size_t IMDBox)::addEventsPartUnsafe(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at)
+  {
+    // --- Go event by event and add them ----
+    typename std::vector<MDE>::const_iterator it = events.begin() + start_at;
+    typename std::vector<MDE>::const_iterator it_end = events.begin() + stop_at;
+    for (; it != it_end; ++it)
+    {
+      //Check out-of-bounds-ness
+      // Event was in bounds; add it
+      addEventUnsafe(*it);
+    }
+
+    return 0;
+  }
+
+  //---------------------------------------------------------------------------------------------------
+  /** Add all of the events contained in a vector, with:
+   * - No bounds checking.
+   * - No thread-safety.
+   *
+   * @param events :: Vector of MDEvent
+   */
+  TMDE(
+  size_t IMDBox)::addEventsUnsafe(const std::vector<MDE> & events)
+  {
+    return this->addEventsPartUnsafe(events, 0, events.size());
+  }
+
+  //---------------------------------------------------------------------------------------------------
+  /** Add all of the events contained in a vector, with:
+   * - Bounds checking.
+   * - Thread-safety.
+   *
+   * @param events :: Vector of MDEvent
+   */
+  TMDE(
+  size_t IMDBox)::addEvents(const std::vector<MDE> & events)
+  {
+    return this->addEventsPart(events, 0, events.size());
+  }
+
+
   //---------------------------------------------------------------------------------------------------
   /** Transform the dimensions contained in this box
    * x' = x*scaling + offset
diff --git a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp
index ebf0caf0e70..1359dda9523 100644
--- a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp
+++ b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp
@@ -464,7 +464,7 @@ namespace MDEvents
 
   //-----------------------------------------------------------------------------------------------
   /** Add a MDLeanEvent to the box.
-   * @param event :: reference to a MDLeanEvent to add.
+   * @param event :: reference to a MDEvent to add.
    * */
   TMDE(
   void MDBox)::addEvent( const MDE & event)
@@ -491,6 +491,33 @@ namespace MDEvents
     dataMutex.unlock();
   }
 
+  //-----------------------------------------------------------------------------------------------
+  /** Add a MDLeanEvent to the box, in a NON-THREAD-SAFE manner.
+   * No lock is performed. This is only safe if no 2 threads will
+   * try to add to the same box at the same time.
+   *
+   * @param event :: reference to a MDEvent to add.
+   * */
+  TMDE(
+  void MDBox)::addEventUnsafe( const MDE & event)
+  {
+    this->data.push_back(event);
+    this->m_dataAdded = true;
+
+#ifdef MDBOX_TRACK_SIGNAL_WHEN_ADDING
+    // Keep the running total of signal and error
+    double signal = event.getSignal();
+    this->m_signal += signal;
+    this->m_errorSquared += event.getErrorSquared();
+#endif
+
+#ifdef MDBOX_TRACKCENTROID_WHENADDING
+    // Running total of the centroid
+    for (size_t d=0; d<nd; d++)
+      this->m_centroid[d] += event.getCenter(d) * signal;
+#endif
+  }
+
   //-----------------------------------------------------------------------------------------------
   /** Add several events. No bounds checking is made!
    *
@@ -500,7 +527,7 @@ namespace MDEvents
    * @return the number of events that were rejected (because of being out of bounds)
    */
   TMDE(
-  size_t MDBox)::addEvents(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at)
+  size_t MDBox)::addEventsPart(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at)
   {
     dataMutex.lock();
     typename std::vector<MDE>::const_iterator start = events.begin()+start_at;
@@ -531,6 +558,24 @@ namespace MDEvents
     return 0;
   }
 
+
+  //-----------------------------------------------------------------------------------------------
+  /** Add several events, within a given range, with no bounds checking,
+   * and not in a thread-safe way
+   *
+   * @param events :: vector of events to be copied.
+   * @param start_at :: index to start at in vector
+   * @param stop_at :: index to stop at in vector (exclusive)
+   * @return the number of events that were rejected (because of being out of bounds)
+   */
+  TMDE(
+  size_t MDBox)::addEventsPartUnsafe(const std::vector<MDE> & events, const size_t start_at, const size_t stop_at)
+  {
+    // The regular MDBox is just as safe/unsafe
+    return this->addEventsPart(events, start_at, stop_at);
+  }
+
+
   //-----------------------------------------------------------------------------------------------
   /** Perform centerpoint binning of events.
    * @param bin :: MDBin object giving the limits of events to accept.
diff --git a/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp b/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp
index 0df3226e5ec..00be1c18f49 100644
--- a/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp
+++ b/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp
@@ -867,12 +867,10 @@ namespace MDEvents
   TMDE(
   inline void MDGridBox)::addEvent( const MDE & event)
   {
-//    std::cout << "\n nd " << nd << "; boxSize[d] " << boxSize[0] << "; min " << this->extents[0].min << "." << std::endl;
     size_t index = 0;
     for (size_t d=0; d<nd; d++)
     {
       coord_t x = event.getCenter(d);
-//      std::cout << x << ":" << ((x - this->extents[d].min) / boxSize[d]) << ",";
       int i = int((x - this->extents[d].min) / boxSize[d]);
       // NOTE: No bounds checking is done (for performance).
       //if (i < 0 || i >= int(split[d])) return;
@@ -884,11 +882,39 @@ namespace MDEvents
     // Add it to the contained box
     if (index < numBoxes) // avoid segfaults for floating point round-off errors.
       boxes[index]->addEvent(event);
-    else
+  }
+
+  //-----------------------------------------------------------------------------------------------
+  /** Add a single MDLeanEvent to the grid box. If the boxes
+   * contained within are also gridded, this will recursively push the event
+   * down to the deepest level.
+   *
+   * Warning! No bounds checking is done (for performance). It must
+   * be known that the event is within the bounds of the grid box before adding.
+   *
+   * Warning! Call is NOT thread-safe. Only 1 thread should be writing to this
+   * box (or any child boxes) at a time
+   *
+   * Note! nPoints, signal and error must be re-calculated using refreshCache()
+   * after all events have been added.
+   *
+   * @param event :: reference to a MDEvent to add.
+   * */
+  TMDE(
+  inline void MDGridBox)::addEventUnsafe(const MDE & event)
+  {
+    size_t index = 0;
+    for (size_t d=0; d<nd; d++)
     {
-      //std::cout << "\nEvent at " << event.getCenter(0) << " is skipped because index is " << index << "\n";
+      coord_t x = event.getCenter(d);
+      int i = int((x - this->extents[d].min) / boxSize[d]);
+      // Accumulate the index
+      index += (i * splitCumul[d]);
     }
 
+    // Add it to the contained box
+    if (index < numBoxes) // avoid segfaults for floating point round-off errors.
+      boxes[index]->addEventUnsafe(event);
   }
 
 
diff --git a/Code/Mantid/Framework/MDEvents/test/IMDBoxTest.h b/Code/Mantid/Framework/MDEvents/test/IMDBoxTest.h
index ab969fee4a2..248aceda5e5 100644
--- a/Code/Mantid/Framework/MDEvents/test/IMDBoxTest.h
+++ b/Code/Mantid/Framework/MDEvents/test/IMDBoxTest.h
@@ -68,6 +68,10 @@ public:
   virtual void addEvent(const MDE & /*point*/)
   {}
 
+  /// Add a single event
+  virtual void addEventUnsafe(const MDE & /*point*/)
+  {}
+
   /** Perform centerpoint binning of events
    * @param bin :: MDBin object giving the limits of events to accept.
    */
diff --git a/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h b/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h
index caa2b18bd32..edfee59db60 100644
--- a/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h
+++ b/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h
@@ -108,6 +108,22 @@ public:
     TS_ASSERT_DELTA( b.getSignal(), 1.2*1, 1e-5);
     TS_ASSERT_DELTA( b.getErrorSquared(), 3.4*1, 1e-5);
   }
+  /** Adding events in unsafe way also works */
+  void test_addEventUnsafe()
+  {
+    MDBox<MDLeanEvent<2>,2> b;
+    MDLeanEvent<2> ev(1.2, 3.4);
+    ev.setCenter(0, 2.0);
+    ev.setCenter(1, 3.0);
+    b.addEventUnsafe(ev);
+    TS_ASSERT_EQUALS( b.getNPoints(), 1)
+#ifndef MDBOX_TRACK_SIGNAL_WHEN_ADDING
+    b.refreshCache();
+#endif
+    // Did it keep a running total of the signal and error?
+    TS_ASSERT_DELTA( b.getSignal(), 1.2*1, 1e-5);
+    TS_ASSERT_DELTA( b.getErrorSquared(), 3.4*1, 1e-5);
+  }
 
 
   /** Add a vector of events */
@@ -143,7 +159,7 @@ public:
     for (size_t i=0; i<10; i++)
       vec.push_back(ev);
 
-    b.addEvents(vec, 5, 8);
+    b.addEventsPart(vec, 5, 8);
 #ifndef MDBOX_TRACK_SIGNAL_WHEN_ADDING
     b.refreshCache();
 #endif
diff --git a/Code/Mantid/Framework/MDEvents/test/MDGridBoxTest.h b/Code/Mantid/Framework/MDEvents/test/MDGridBoxTest.h
index 09ba88decd6..ca156723166 100644
--- a/Code/Mantid/Framework/MDEvents/test/MDGridBoxTest.h
+++ b/Code/Mantid/Framework/MDEvents/test/MDGridBoxTest.h
@@ -773,7 +773,7 @@ public:
       }
 
     size_t numbad = 0;
-    TS_ASSERT_THROWS_NOTHING( numbad = b->addEvents( events, 50, 60 ); );
+    TS_ASSERT_THROWS_NOTHING( numbad = b->addEventsPart( events, 50, 60 ); );
     // Get the right totals again
     b->refreshCache(NULL);
     TS_ASSERT_EQUALS( numbad, 0);
diff --git a/Code/Mantid/Framework/NexusCPP/inc/MantidNexusCPP/NeXusFile.hpp b/Code/Mantid/Framework/NexusCPP/inc/MantidNexusCPP/NeXusFile.hpp
index 9c7cfe6d29d..400d9e639e7 100644
--- a/Code/Mantid/Framework/NexusCPP/inc/MantidNexusCPP/NeXusFile.hpp
+++ b/Code/Mantid/Framework/NexusCPP/inc/MantidNexusCPP/NeXusFile.hpp
@@ -175,6 +175,12 @@ namespace NeXus {
     template<typename NumT>
     void free(NumT*& data);
 
+    /** Return the C-API handle to the open file */
+    NXhandle getHandle()
+    {
+      return m_file_id;
+    }
+
     /**
      * Create a new group.
      *
-- 
GitLab