Skip to content
Snippets Groups Projects
MDGridBoxTest.h 64.2 KiB
Newer Older
    for (size_t i = 0; i < num_repeat; i++) {
      // Make an event in the middle of each box
      double centers[2] = {1e-10, 1e-10};
      events.push_back(MDLeanEvent<2>(2.0, 2.0, centers));
    TS_ASSERT_THROWS_NOTHING(b0->addEvents(events););
    TS_ASSERT_THROWS_NOTHING(b0->splitAllIfNeeded(NULL);)

    // Dig recursively into the gridded box hierarchies
    std::vector<ibox_t *> boxes;
    gbox_t *b = b0;
      expected_depth++;
      boxes = b->getBoxes();

      // Get the 0th box
      b = dynamic_cast<gbox_t *>(boxes[0]);

      // The 0-th box is a MDGridBox (it was split)
      // (though it is normal for b to be a MDBox when you reach the max depth)
      if (expected_depth < 4) {
        TS_ASSERT(b)
      }
      TS_ASSERT_EQUALS(boxes[0]->getNPoints(), num_repeat);
      // The 0-th box is at the expected_depth
      TS_ASSERT_EQUALS(boxes[0]->getDepth(), expected_depth);
      TS_ASSERT_EQUALS(boxes[1]->getNPoints(), 0);
      // The other box is a MDBox (it was not split)
      TS_ASSERT(dynamic_cast<box_t *>(boxes[1]))
    // We went this many levels (and no further) because recursion depth is
    // limited
    TS_ASSERT_EQUALS(boxes[0]->getDepth(), 4);
    // clean up  behind
    BoxController *const bcc = b0->getBoxController();
  //------------------------------------------------------------------------------------------------
  /** This test splits a large number of events, and uses a ThreadPool
   * to use all cores.
   */
  void test_splitAllIfNeeded_usingThreadPool() {
    typedef MDGridBox<MDLeanEvent<2>, 2> gbox_t;
    typedef MDBoxBase<MDLeanEvent<2>, 2> ibox_t;
    gbox_t *b = MDEventsTestHelper::makeMDGridBox<2>();
    b->getBoxController()->setSplitThreshold(100);
    b->getBoxController()->setMaxDepth(4);

    // Make a 1000 events in each sub-box
    size_t num_repeat = 1000;
    if (DODEBUG)
      num_repeat = 2000;
    if (DODEBUG)
      std::cout << "Adding " << num_repeat * 100 << " events...\n";
    MDEventsTestHelper::feedMDBox<2>(b, num_repeat, 10, 0.5, 1.0);
    if (DODEBUG)
      std::cout << "Adding events done in " << tim.elapsed() << "!\n";
    ThreadSchedulerFIFO *ts = new ThreadSchedulerFIFO();
    if (DODEBUG)
      std::cout << "Splitting events done in " << tim.elapsed() << " sec.\n";
    // Now check the results. Each sub-box should be MDGridBox and have that
    // many events
    std::vector<ibox_t *> boxes = b->getBoxes();
    for (size_t i = 0; i < boxes.size(); i++) {
      ibox_t *box = boxes[i];
      TS_ASSERT_EQUALS(box->getNPoints(), num_repeat);
      TS_ASSERT(dynamic_cast<gbox_t *>(box));

      size_t numChildren = box->getNumChildren();
      if (numChildren > 0) {
        size_t lastId = box->getChild(0)->getID();
        for (size_t i = 1; i < numChildren; i++) {
          TSM_ASSERT_EQUALS("Children IDs need to be sequential!",
                            box->getChild(i)->getID(), lastId + 1);
          lastId = box->getChild(i)->getID();
        }
      }
    }
    // clean up  behind
    BoxController *const bcc = b->getBoxController();
    delete b;
    delete bcc;
  //------------------------------------------------------------------------------------------------
  /** Helper to make a 2D MDBin */
  MDBin<MDLeanEvent<2>, 2> makeMDBin2(double minX, double maxX, double minY,
                                      double maxY) {
    MDBin<MDLeanEvent<2>, 2> bin;
    bin.m_min[0] = static_cast<coord_t>(minX);
    bin.m_max[0] = static_cast<coord_t>(maxX);
    bin.m_min[1] = static_cast<coord_t>(minY);
    bin.m_max[1] = static_cast<coord_t>(maxY);
  //------------------------------------------------------------------------------------------------
  /** Helper to test the binning of a 2D bin */
  void doTestMDBin2(MDGridBox<MDLeanEvent<2>, 2> *b, const std::string &message,
                    double minX, double maxX, double minY, double maxY,
                    double expectedSignal) {
    //    std::cout << "Bins: X " << std::setw(5) << minX << " to "<<
    //    std::setw(5)  << maxX << ", Y " << std::setw(5) << minY << " to "<<
    //    std::setw(5)  << maxY      << ". " << message << '\n';

    MDBin<MDLeanEvent<2>, 2> bin;
    bin = makeMDBin2(minX, maxX, minY, maxY);
    TSM_ASSERT_DELTA(message, bin.m_signal, expectedSignal, 1e-5);
  }

  //------------------------------------------------------------------------------------------------
  /** Test binning in orthogonal axes */
  void test_centerpointBin() {
    typedef MDGridBox<MDLeanEvent<2>, 2> gbox_t;
    // 10x10 bins, 2 events per bin, each weight of 1.0
    gbox_t *b = MDEventsTestHelper::makeMDGridBox<2>();
    MDEventsTestHelper::feedMDBox<2>(b, 2);
    TS_ASSERT_DELTA(b->getSignal(), 200.0, 1e-5);
    MDBin<MDLeanEvent<2>, 2> bin;
    doTestMDBin2(b, "Bin that is completely off", 10.1, 11.2, 1.9, 3.12, 0.0);
    doTestMDBin2(b, "Bin that is completely off (2)", 2, 3, -0.6, -0.1, 0.0);
    doTestMDBin2(b, "Bin that holds one entire MDBox (bigger than it)", 0.8,
                 2.2, 1.9, 3.12, 2.0);

    doTestMDBin2(b, "Bin that holds one entire MDBox (going off one edge)",
                 -0.2, 1.2, 1.9, 3.12, 2.0);
    doTestMDBin2(b,
                 "Bin that holds one entire MDBox (going off the other edge)",
                 8.9, 10.2, 1.9, 3.12, 2.0);

    doTestMDBin2(b, "Bin that holds one entire MDBox (going off both edge)",
                 -0.2, 1.2, -0.2, 1.2, 2.0);
    doTestMDBin2(b, "Bin that holds one entire MDBox and a fraction of at "
                    "least one more with something",
                 0.8, 2.7, 1.9, 3.12, 4.0);
    doTestMDBin2(b, "Bin that holds four entire MDBoxes", 0.8, 3.1, 0.9, 3.2,
                 8.0);
    doTestMDBin2(b, "Bin goes off two edges in one direction", -0.3, 10.2, 1.9,
                 3.1, 10 * 2.0);
    doTestMDBin2(
        b, "Bin that fits all within a single MDBox, and contains the center",
        0.2, 0.8, 0.2, 0.8, 2.0);
    doTestMDBin2(b, "Bin that fits all within a single MDBox, and DOES NOT "
                    "contain anything",
                 0.2, 0.3, 0.1, 0.2, 0.0);
    doTestMDBin2(b, "Bin that fits partially in two MDBox'es, and DOES NOT "
                    "contain anything",
                 0.8, 1.2, 0.1, 0.2, 0.0);
    doTestMDBin2(
        b, "Bin that fits partially in two MDBox'es, and contains the centers",
        0.2, 1.8, 0.1, 0.9, 4.0);
    doTestMDBin2(
        b, "Bin that fits partially in one MDBox'es, and goes of the edge",
        -3.2, 0.8, 0.1, 0.9, 2.0);
    delete b->getBoxController();
    delete b;
  //------------------------------------------------------------------------------------------------
  /** For test_integrateSphere
   *
   * @param box
   * @param radius :: radius to integrate
   * @param numExpected :: how many events should be in there
   */
  void do_check_integrateSphere(MDGridBox<MDLeanEvent<2>, 2> &box, double x,
                                double y, const double radius,
                                double numExpected, std::string message) {
    bool dimensionsUsed[2] = {true, true};
    coord_t center[2] = {static_cast<coord_t>(x), static_cast<coord_t>(y)};
    CoordTransformDistance sphere(2, center, dimensionsUsed);

    signal_t signal = 0;
    signal_t errorSquared = 0;
    box.integrateSphere(sphere, static_cast<coord_t>(radius * radius), signal,
                        errorSquared);
    TSM_ASSERT_DELTA(message, signal, 1.0 * numExpected, 1e-5);
    TSM_ASSERT_DELTA(message, errorSquared, 1.0 * numExpected, 1e-5);
  void do_test_integrateSphere(MDGridBox<MDLeanEvent<2>, 2> *box_ptr) {
    MDGridBox<MDLeanEvent<2>, 2> &box = *box_ptr;
    TS_ASSERT_EQUALS(box.getNPoints(), 10 * 10);

    do_check_integrateSphere(box, 4.5, 4.5, 0.5, 1.0,
                             "Too small to contain any vertices");
    do_check_integrateSphere(box, 4.5, 4.5, 0.001, 1.0,
                             "Tiny but still has an event.");
    do_check_integrateSphere(box, 4.51, 4.5, 0.001, 0.0,
                             "Tiny but off the event.");
    do_check_integrateSphere(box, 2.0, 2.0, 0.49, 0.0,
                             "At a corner but grabbing nothing");
    do_check_integrateSphere(box, 4.8, 4.5, 0.35, 1.0,
                             "Too small to contain any vertices");
    do_check_integrateSphere(box, 5.0, 5.0, 1.0, 4.0,
                             "At a corner, containing 4 neighbors");
    do_check_integrateSphere(box, 4.5, 4.5, 0.9, 1.0,
                             "Contains one box completely");
    do_check_integrateSphere(box, 0.5, 0.5, 0.9, 1.0,
                             "Contains one box completely, at the edges");
    do_check_integrateSphere(box, 9.5, 0.5, 0.9, 1.0,
                             "Contains one box completely, at the edges");
    do_check_integrateSphere(box, 0.5, 9.5, 0.9, 1.0,
                             "Contains one box completely, at the edges");
    do_check_integrateSphere(box, 4.5, 9.5, 0.9, 1.0,
                             "Contains one box completely, at the edges");
    do_check_integrateSphere(box, 9.5, 9.5, 0.9, 1.0,
                             "Contains one box completely, at the edges");
    do_check_integrateSphere(
        box, 1.5, 1.5, 1.95, 9.0,
        "Contains 5 boxes completely, and 4 boxes with a point");
    do_check_integrateSphere(box, -1.0, 0.5, 1.55, 1.0,
                             "Off an edge but enough to get an event");
    double center[2] = {0.001, 0.5};
    do_check_integrateSphere(
        box, -1.0, 0.5, 1.01, 1.0,
        "Off an edge but just barely enough to get an event");
    do_check_integrateSphere(box, 0.0, 0.5, 0.01, 1.0,
                             "Tiny, but just barely enough to get an event");
  }

  /** Test of sphere integration with even splitting*/
  void test_integrateSphere() {
    MDGridBox<MDLeanEvent<2>, 2> *box_ptr =
        MDEventsTestHelper::makeMDGridBox<2>();
    MDEventsTestHelper::feedMDBox<2>(box_ptr, 1);
    // clean up  behind
    BoxController *const bcc = box_ptr->getBoxController();
    delete box_ptr;
    delete bcc;
  void test_integrateSphere_unevenSplit() {
    MDGridBox<MDLeanEvent<2>, 2> *box_ptr =
        MDEventsTestHelper::makeMDGridBox<2>(10, 5);
    MDEventsTestHelper::feedMDBox<2>(box_ptr, 1);
    // clean up  behind
    BoxController *const bcc = box_ptr->getBoxController();
    delete box_ptr;
    delete bcc;
  void test_integrateSphere_unevenSplit2() {
    MDGridBox<MDLeanEvent<2>, 2> *box_ptr =
        MDEventsTestHelper::makeMDGridBox<2>(3, 7);
    MDEventsTestHelper::feedMDBox<2>(box_ptr, 1);
    // clean up  behind
    BoxController *const bcc = box_ptr->getBoxController();
    delete box_ptr;
    delete bcc;
  /** Had a really-hard to find bug where the tests worked only
   * if the extents started at 0.0.
   * This test has a box from -10.0 to +10.0 to check for that
   */
  void test_integrateSphere_dimensionsDontStartAtZero() {
    MDGridBox<MDLeanEvent<2>, 2> *box_ptr =
        MDEventsTestHelper::makeMDGridBox<2>(10, 10, -10.0);
    MDEventsTestHelper::feedMDBox<2>(box_ptr, 1, 10, -9.0, 2.0);
    MDGridBox<MDLeanEvent<2>, 2> &box = *box_ptr;
    TS_ASSERT_EQUALS(box.getNPoints(), 10 * 10);
    do_check_integrateSphere(box, 1.0, 1.0, 1.45, 1.0,
                             "Contains one box completely");
    do_check_integrateSphere(box, 9.0, 9.0, 1.45, 1.0,
                             "Contains one box completely, at the edges");
    // clean up  behind
    BoxController *const bcc = box_ptr->getBoxController();
    delete box_ptr;
    delete bcc;
  }

  //------------------------------------------------------------------------------------------------
  /** For test_integrateSphere3d
   *
   * @param box
   * @param radius :: radius to integrate
   * @param numExpected :: how many events should be in there
   */
  void do_check_integrateSphere3d(MDGridBox<MDLeanEvent<3>, 3> &box, double x,
                                  double y, double z, const double radius,
                                  double numExpected, std::string message) {
    bool dimensionsUsed[3] = {true, true, true};
    coord_t center[3] = {static_cast<coord_t>(x), static_cast<coord_t>(y),
                         static_cast<coord_t>(z)};
    CoordTransformDistance sphere(3, center, dimensionsUsed);

    signal_t signal = 0;
    signal_t errorSquared = 0;
    box.integrateSphere(sphere, static_cast<coord_t>(radius * radius), signal,
                        errorSquared);
    TSM_ASSERT_DELTA(message, signal, 1.0 * numExpected, 1e-5);
    TSM_ASSERT_DELTA(message, errorSquared, 1.0 * numExpected, 1e-5);
  }

  //------------------------------------------------------------------------------------------------
  void test_integrateSphere3d() {
    MDGridBox<MDLeanEvent<3>, 3> *box_ptr =
        MDEventsTestHelper::makeMDGridBox<3>();
    MDEventsTestHelper::feedMDBox<3>(box_ptr, 1);
    MDGridBox<MDLeanEvent<3>, 3> &box = *box_ptr;
    TS_ASSERT_EQUALS(box.getNPoints(), 10 * 10 * 10);

    do_check_integrateSphere3d(box, 0.5, 0.5, 0.5, 0.9, 1.0,
                               "Contains one box completely, at a corner");
    do_check_integrateSphere3d(box, 9.5, 9.5, 9.5, 0.9, 1.0,
                               "Contains one box completely, at a corner");
    do_check_integrateSphere3d(
        box, 9.5, 9.5, 9.5, 0.85, 1.0,
        "Does NOT contain one box completely, at a corner");
    do_check_integrateSphere3d(box, 9.0, 9.0, 9.0, 1.75, 20.0,
                               "Contains 8 boxes completely, at a corner");
    do_check_integrateSphere3d(
        box, 9.0, 9.0, 9.0, 1.70, 20.0,
        "Does NOT contains one box completely, at a corner");
    double center[3] = {0.001, 0.5, 0.5};
    //    do_check_integrateSphere(box, -1.0,0.5, 1.01,  1.0, "Off an edge but
    //    just barely enough to get an event");
    //    do_check_integrateSphere(box, 0.0,0.5, 0.01,  1.0, "Tiny, but just
    //    barely enough to get an event");
    delete box_ptr->getBoxController();
    delete box_ptr;
  //------------------------------------------------------------------------------------------------
  /** For test_integrateSphere
   *
   * @param box
   * @param radius :: radius to integrate
   * @param xExpected :: expected centroid
   * @param yExpected :: expected centroid
   */
  void do_check_centroidSphere(MDGridBox<MDLeanEvent<2>, 2> &box, double x,
                               double y, const double radius,
                               double numExpected, double xExpected,
                               double yExpected, std::string message) {
    bool dimensionsUsed[2] = {true, true};
    coord_t center[2] = {static_cast<coord_t>(x), static_cast<coord_t>(y)};
    CoordTransformDistance sphere(2, center, dimensionsUsed);

    box.centroidSphere(sphere, static_cast<coord_t>(radius * radius), centroid,
                       signal);
    if (signal != 0.0) {
      for (size_t d = 0; d < 2; d++)
        centroid[d] /= static_cast<coord_t>(signal);
    TSM_ASSERT_DELTA(message, signal, 1.0 * numExpected, 1e-5);
    TSM_ASSERT_DELTA(message, centroid[0], xExpected, 1e-5);
    TSM_ASSERT_DELTA(message, centroid[1], yExpected, 1e-5);
  void test_centroidSphere() {
    MDGridBox<MDLeanEvent<2>, 2> *box_ptr =
        MDEventsTestHelper::makeMDGridBox<2>();
    MDEventsTestHelper::feedMDBox<2>(box_ptr, 1);
    MDGridBox<MDLeanEvent<2>, 2> &box = *box_ptr;
    TS_ASSERT_EQUALS(box.getNPoints(), 10 * 10);

    do_check_centroidSphere(box, 4.5, 4.5, 0.5, 1.0, 4.5, 4.5,
                            "Too small to contain any vertices");
    do_check_centroidSphere(box, 4.5, 4.5, 0.001, 1.0, 4.5, 4.5,
                            "Tiny but still has an event.");
    do_check_centroidSphere(box, 4.51, 4.5, 0.001, 0.0, 0.0, 0.0,
                            "Tiny but off the event.");
    do_check_centroidSphere(box, 2.0, 2.0, 0.49, 0.0, 0.0, 0.0,
                            "At a corner but grabbing nothing");
    do_check_centroidSphere(box, 4.8, 4.5, 0.35, 1.0, 4.5, 4.5,
                            "Too small to contain any vertices");
    do_check_centroidSphere(box, 5.0, 5.0, 1.0, 4.0, 5.0, 5.0,
                            "At a corner, containing 4 neighbors");
    do_check_centroidSphere(box, 4.5, 4.5, 0.9, 1.0, 4.5, 4.5,
                            "Contains one box completely");
    do_check_centroidSphere(box, 0.5, 0.5, 0.9, 1.0, 0.5, 0.5,
                            "Contains one box completely, at the edges");
    do_check_centroidSphere(box, 9.5, 0.5, 0.9, 1.0, 9.5, 0.5,
                            "Contains one box completely, at the edges");
    do_check_centroidSphere(box, 0.5, 9.5, 0.9, 1.0, 0.5, 9.5,
                            "Contains one box completely, at the edges");
    do_check_centroidSphere(box, 4.5, 9.5, 0.9, 1.0, 4.5, 9.5,
                            "Contains one box completely, at the edges");
    do_check_centroidSphere(box, 9.5, 9.5, 0.9, 1.0, 9.5, 9.5,
                            "Contains one box completely, at the edges");
    do_check_centroidSphere(
        box, 1.5, 1.5, 1.95, 9.0, 1.5, 1.5,
        "Contains 5 boxes completely, and 4 boxes with a point");
    do_check_centroidSphere(box, -1.0, 0.5, 1.55, 1.0, 0.5, 0.5,
                            "Off an edge but enough to get an event");
    double center[2] = {0.001, 0.5};
    do_check_integrateSphere(
        box, -1.0, 0.5, 1.01, 1.0,
        "Off an edge but just barely enough to get an event");
    do_check_integrateSphere(box, 0.0, 0.5, 0.01, 1.0,
                             "Tiny, but just barely enough to get an event");

    delete box_ptr->getBoxController();
    delete box_ptr;
  void test_getIsMasked_WhenNoMasking() {
    std::vector<API::IMDNode *> boxes;
    MockMDBox *a = new MockMDBox;
    MockMDBox *b = new MockMDBox;
    EXPECT_CALL(*a, getIsMasked())
        .Times(1)
        .WillOnce(Return(false)); // Not masked
    EXPECT_CALL(*b, getIsMasked())
        .Times(1)
        .WillOnce(Return(false)); // Not masked
    boxes.push_back(a);
    boxes.push_back(b);
Peterson, Peter's avatar
Peterson, Peter committed
    auto bc = boost::make_shared<BoxController>(1);
    std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extentsVector(1);
    MDGridBox<MDLeanEvent<1>, 1> g(bc, 0, extentsVector);
    g.setChildren(boxes, 0, 2);
    TSM_ASSERT("No inner boxes were masked so the MDGridBox should not report "
               "that it is masked",
               !g.getIsMasked());
    TS_ASSERT(Mock::VerifyAndClearExpectations(a));
    TS_ASSERT(Mock::VerifyAndClearExpectations(b));
  }
  void test_getIsMasked_WhenFirstMasked() {
    std::vector<API::IMDNode *> boxes;
    MockMDBox *a = new MockMDBox;
    MockMDBox *b = new MockMDBox;
    EXPECT_CALL(*a, getIsMasked()).Times(1).WillOnce(Return(true)); // MASKED
    EXPECT_CALL(*b, getIsMasked())
        .Times(0); // Not masked, but will never be called.
    boxes.push_back(a);
    boxes.push_back(b);
Peterson, Peter's avatar
Peterson, Peter committed
    auto bc = boost::make_shared<BoxController>(1);
    std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extentsVector(1);
    MDGridBox<MDLeanEvent<1>, 1> g(bc, 0, extentsVector);
    g.setChildren(boxes, 0, 2);
    TSM_ASSERT("First inner box masked, so should return masked",
               g.getIsMasked());
    TS_ASSERT(Mock::VerifyAndClearExpectations(a));
    TS_ASSERT(Mock::VerifyAndClearExpectations(b));
  }
  void test_getIsMasked_WhenLastMasked() {
    std::vector<API::IMDNode *> boxes;
    MockMDBox *a = new MockMDBox;
    MockMDBox *b = new MockMDBox;
    EXPECT_CALL(*a, getIsMasked())
        .Times(1)
        .WillOnce(Return(false)); // NOT MASKED
    EXPECT_CALL(*b, getIsMasked()).Times(1).WillOnce(Return(true)); // MASKED
    boxes.push_back(a);
    boxes.push_back(b);
Peterson, Peter's avatar
Peterson, Peter committed
    auto bc = boost::make_shared<BoxController>(1);
    std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extentsVector(1);
    MDGridBox<MDLeanEvent<1>, 1> g(bc, 0, extentsVector);
    g.setChildren(boxes, 0, 2);
    TSM_ASSERT("Second inner box masked, so should return masked",
               g.getIsMasked());
    TS_ASSERT(Mock::VerifyAndClearExpectations(a));
    TS_ASSERT(Mock::VerifyAndClearExpectations(b));
  }
  void test_mask() {
    std::vector<API::IMDNode *> boxes;
    MockMDBox *a = new MockMDBox;
    MockMDBox *b = new MockMDBox;
    EXPECT_CALL(*a, mask()).Times(1);
    EXPECT_CALL(*b, mask()).Times(1);
    boxes.push_back(a);
    boxes.push_back(b);
Peterson, Peter's avatar
Peterson, Peter committed
    auto bc = boost::make_shared<BoxController>(1);
    std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extentsVector(1);
    MDGridBox<MDLeanEvent<1>, 1> griddedBox(bc, 0, extentsVector);
    griddedBox.setChildren(boxes, 0, 2);
    TS_ASSERT_THROWS_NOTHING(griddedBox.mask()); // Mask the gridded box
    TS_ASSERT(Mock::VerifyAndClearExpectations(a));
    TS_ASSERT(Mock::VerifyAndClearExpectations(b));
  }
  void test_unmask() {
    std::vector<API::IMDNode *> boxes;
    MockMDBox *a = new MockMDBox;
    MockMDBox *b = new MockMDBox;
    EXPECT_CALL(*a, unmask()).Times(1);
    EXPECT_CALL(*b, unmask()).Times(1);
    boxes.push_back(a);
    boxes.push_back(b);
Peterson, Peter's avatar
Peterson, Peter committed
    auto bc = boost::make_shared<BoxController>(1);
    std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extentsVector(1);
    MDGridBox<MDLeanEvent<1>, 1> griddedBox(bc, 0, extentsVector);
    griddedBox.setChildren(boxes, 0, 2);
    TS_ASSERT_THROWS_NOTHING(griddedBox.unmask()); // Un-Mask the gridded box
    TS_ASSERT(Mock::VerifyAndClearExpectations(a));
    TS_ASSERT(Mock::VerifyAndClearExpectations(b));
  }
private:
  std::string message;
//=====================================================================================
//===================================== Performance Test
//==============================
//=====================================================================================
class MDGridBoxTestPerformance : public CxxTest::TestSuite {
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
  static MDGridBoxTestPerformance *createSuite() {
    return new MDGridBoxTestPerformance();
  }
  static void destroySuite(MDGridBoxTestPerformance *suite) { delete suite; }
  MDGridBox<MDLeanEvent<3>, 3> *box3;
  MDGridBox<MDLeanEvent<3>, 3> *box3b;
  std::vector<MDLeanEvent<3>> events;
  MDGridBox<MDLeanEvent<1>, 1> *recursiveParent;
  MDGridBox<MDLeanEvent<1>, 1> *recursiveParent2;
  MDGridBoxTestPerformance() {
    box3b = MDEventsTestHelper::makeRecursiveMDGridBox<3>(5, 1);

    // Make the list of fake events, random dist.
    size_t num = 1000000;
    boost::uniform_real<double> u(0, 5.0); // Range
    boost::variate_generator<boost::mt19937 &, boost::uniform_real<double>> gen(
        rng, u);
    for (size_t i = 0; i < num; ++i) {
      for (size_t d = 0; d < 3; d++)
        centers[d] = gen();
      // Create and add the event.
      events.push_back(MDLeanEvent<3>(1.0, 1.0, centers));
    box3b->addEvents(events);
    box3b->refreshCache();
    // Recursively gridded box with 1,111,111 boxes total.
    recursiveParent = MDEventsTestHelper::makeRecursiveMDGridBox<1>(10, 6);
    // Recursively gridded box with 111,111 boxes total.
    recursiveParent2 = MDEventsTestHelper::makeRecursiveMDGridBox<1>(10, 5);
  ~MDGridBoxTestPerformance() override { delete box3b; }
  void setUp() override {
    box3 = MDEventsTestHelper::makeRecursiveMDGridBox<3>(5, 1);
  void tearDown() override { delete box3; }
  void test_refreshCache() { box3b->refreshCache(); }
  /** Performance test that adds lots of events to a recursively split box.
   * SINGLE-THREADED!
   */
  void test_addEvents_lots() {
    TS_ASSERT_EQUALS(box3->getBoxController()->getTotalNumMDBoxes(),
                     125 * 125 + 1); // +1 might be a test issue
    TS_ASSERT_EQUALS(events.size(), 1e6);
    for (size_t i = 0; i < 5; ++i) {
  //-----------------------------------------------------------------------------
  /** Do a sphere integration
   *
   * @param center :: coordinate of the center
   * @param radius :: radius
   */
  void do_test_sphereIntegrate(coord_t *center, coord_t radius,
                               double expectSignal, double tol) {
    bool dimensionsUsed[3] = {true, true, true};
    CoordTransformDistance sphere(3, center, dimensionsUsed);

    for (size_t i = 0; i < 1000; i++) {
      box3b->integrateSphere(sphere, radius * radius, signal, errorSquared);
    TS_ASSERT_DELTA(signal, expectSignal, tol);
  /** Smallish sphere in the middle goes partially through lots of boxes */
  void test_sphereIntegrate_inTheMiddle() {
    do_test_sphereIntegrate(center, 1.0, (1e6 / 125) * (4.0 * M_PI / 3.0),
                            2000.0);
  /** Huge sphere containing all within */
  void test_sphereIntegrate_inTheMiddle_largeSphere() {
    coord_t center[3] = {2.5, 2.5, 2.5};
    do_test_sphereIntegrate(center, 5.0, 1e6, 1e-3);
  void test_sphereIntegrate_OffTheBox() {
    coord_t center[3] = {11., 5., 5.};
    do_test_sphereIntegrate(center, 1.0, 0.0, 1e-3);
  }

  //-----------------------------------------------------------------------------
  /** Do a sphere centroiding
   *
   * @param center :: coordinate of the center
   * @param radius :: radius
   */
  void do_test_sphereCentroid(coord_t *center, coord_t radius,
                              double expectSignal, double tol) {
    bool dimensionsUsed[3] = {true, true, true};
    CoordTransformDistance sphere(3, center, dimensionsUsed);

    for (size_t i = 0; i < 100; i++) {
      for (size_t d = 0; d < 3; d++)
      box3b->centroidSphere(sphere, radius * radius, centroid, signal);
      if (signal != 0.0) {
        for (size_t d = 0; d < 3; d++)
          centroid[d] /= static_cast<coord_t>(signal);
    // The expected number of events, given a sphere of radius "radius"
    TS_ASSERT_DELTA(signal, expectSignal, tol);

    if (expectSignal > 0.0) {
      // And the centroid should be close to the sphere center
      for (size_t d = 0; d < 3; d++)
        TS_ASSERT_DELTA(centroid[d], center[d], 1e-2);
    }
  /** Smallish sphere in the middle goes partially through lots of boxes */
  void test_sphereCentroid_inTheMiddle() {
    do_test_sphereCentroid(center, 1.0, (1e6 / 125) * (4.0 * M_PI / 3.0), 2000);
  void test_sphereCentroid_inTheMiddle_largeSphere() {
    coord_t center[3] = {2.5, 2.5, 2.5};
    do_test_sphereCentroid(center, 5.0, 1e6, 1e-3);
  }

  /** Peak that is off the box entirely */
  void test_sphereCentroid_OffTheBox() {
    coord_t center[3] = {11., 5., 5.};
    do_test_sphereCentroid(center, 1.0, 0.0, 1e-3);
  }

  /** Recursive getting of a list of MDBoxBase.
  void test_getBoxes() {
    std::vector<API::IMDNode *> boxes;
    for (size_t i = 0; i < 10; i++) {
      TS_ASSERT_EQUALS(boxes.size(), 1111111);
      TS_ASSERT_EQUALS(boxes[0], recursiveParent);