Skip to content
Snippets Groups Projects
ReflectometrySumInQTest.h 15.5 KiB
Newer Older
#ifndef MANTID_ALGORITHMS_REFLECTOMETRYSUMINQTEST_H_
#define MANTID_ALGORITHMS_REFLECTOMETRYSUMINQTEST_H_

#include <cxxtest/TestSuite.h>

#include "MantidAlgorithms/ReflectometrySumInQ.h"

#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/SpectrumInfo.h"
#include "MantidHistogramData/HistogramIterator.h"
#include "MantidTestHelpers/WorkspaceCreationHelper.h"

using Mantid::Algorithms::ReflectometrySumInQ;

class ReflectometrySumInQTest : public CxxTest::TestSuite {
public:
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
  static ReflectometrySumInQTest *createSuite() {
    return new ReflectometrySumInQTest();
  }
  static void destroySuite(ReflectometrySumInQTest *suite) { delete suite; }
  static Mantid::API::MatrixWorkspace_sptr
  convertToWavelength(Mantid::API::MatrixWorkspace_sptr ws) {
    auto toWavelength =
        API::AlgorithmManager::Instance().createUnmanaged("ConvertUnits");
    toWavelength->initialize();
    toWavelength->setChild(true);
    toWavelength->setProperty("InputWorkspace", ws);
    toWavelength->setProperty("OutputWorkspace", "_unused_for_child");
    toWavelength->setProperty("Target", "Wavelength");
    toWavelength->setProperty("EMode", "Elastic");
    toWavelength->execute();
    return toWavelength->getProperty("OutputWorkspace");
  }

  static Mantid::API::MatrixWorkspace_sptr
  detectorsOnly(Mantid::API::MatrixWorkspace_sptr ws) {
    using namespace Mantid;
    auto &specturmInfo = ws->spectrumInfo();
    std::vector<size_t> detectorIndices;
    for (size_t i = 0; i < ws->getNumberHistograms(); ++i) {
      if (specturmInfo.isMonitor(i)) {
        continue;
      }
      detectorIndices.emplace_back(i);
    }
    auto extractDetectors =
        API::AlgorithmManager::Instance().createUnmanaged("ExtractSpectra");
    extractDetectors->initialize();
    extractDetectors->setChild(true);
    extractDetectors->setProperty("InputWorkspace", ws);
    extractDetectors->setProperty("OutputWorkspace", "_unused_for_child");
    extractDetectors->setProperty("WorkspaceIndexList", detectorIndices);
    extractDetectors->execute();
    return extractDetectors->getProperty("OutputWorkspace");
  }
  void test_init() {
    TS_ASSERT_THROWS_NOTHING(alg.initialize())
    TS_ASSERT(alg.isInitialized())
  void test_sumSingleHistogram() {
    using namespace Mantid;
    auto inputWS = testWorkspace();
    inputWS = detectorsOnly(inputWS);
    inputWS = convertToWavelength(inputWS);
    const auto &Ys = inputWS->y(0);
    const auto totalY = std::accumulate(Ys.cbegin(), Ys.cend(), 0.);
    const std::array<bool, 2> flatSampleOptions{{true, false}};
    for (const auto isFlatSample : flatSampleOptions) {
      for (size_t i = 0; i < inputWS->getNumberHistograms(); ++i) {
        ReflectometrySumInQ alg;
        alg.setChild(true);
        alg.setRethrows(true);
        TS_ASSERT_THROWS_NOTHING(alg.initialize())
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputWS))
        TS_ASSERT_THROWS_NOTHING(
            alg.setPropertyValue("InputWorkspaceIndexSet", std::to_string(i)))
        TS_ASSERT_THROWS_NOTHING(
            alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
        TS_ASSERT_THROWS_NOTHING(
            alg.setProperty("BeamCentre", static_cast<int>(i)))
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("FlatSample", isFlatSample))
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("IncludePartialBins", true))
        TS_ASSERT_THROWS_NOTHING(alg.execute())
        API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace");
        TS_ASSERT(outputWS);
        TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1)
        auto &Ys = outputWS->y(0);
        const auto totalYSummedInQ =
            std::accumulate(Ys.cbegin(), Ys.cend(), 0.0);
        TS_ASSERT_DELTA(totalYSummedInQ, totalY, 1e-10)
      }
    }
  }

  void test_sumEntireWorkspace() {
    using namespace Mantid;
    auto inputWS = testWorkspace();
    inputWS = detectorsOnly(inputWS);
    inputWS = convertToWavelength(inputWS);
    auto &Ys = inputWS->y(0);
    double totalY{0.0};
    for (size_t i = 0; i < inputWS->getNumberHistograms(); ++i) {
      totalY += std::accumulate(Ys.cbegin(), Ys.cend(), 0.0);
    }
    const std::array<bool, 2> flatSampleOptions{{true, false}};
    for (const auto isFlatSample : flatSampleOptions) {
      // Loop over possible beam centres.
      for (size_t beamCentre = 0; beamCentre < inputWS->getNumberHistograms();
           ++beamCentre) {
        ReflectometrySumInQ alg;
        alg.setChild(true);
        alg.setRethrows(true);
        TS_ASSERT_THROWS_NOTHING(alg.initialize())
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputWS))
        TS_ASSERT_THROWS_NOTHING(
            alg.setPropertyValue("InputWorkspaceIndexSet", "0, 1, 2"))
        TS_ASSERT_THROWS_NOTHING(
            alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
        TS_ASSERT_THROWS_NOTHING(
            alg.setProperty("BeamCentre", static_cast<int>(beamCentre)))
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("FlatSample", isFlatSample))
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("IncludePartialBins", true))
        TS_ASSERT_THROWS_NOTHING(alg.execute())
        API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace");
        TS_ASSERT(outputWS);
        TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1)
        auto &Ys = outputWS->y(0);
        const auto totalYSummedInQ =
            std::accumulate(Ys.cbegin(), Ys.cend(), 0.0);
        TS_ASSERT_DELTA(totalYSummedInQ, totalY, 1e-10)
      }
    }
  }
  void test_excludePartialBins() {
    using namespace Mantid;
    auto inputWS = testWorkspace();
    inputWS = detectorsOnly(inputWS);
    inputWS = convertToWavelength(inputWS);
    const std::array<bool, 2> flatSampleOptions{{true, false}};
    for (const auto isFlatSample : flatSampleOptions) {
      for (size_t i = 0; i < inputWS->getNumberHistograms(); ++i) {
        ReflectometrySumInQ alg;
        alg.setChild(true);
        alg.setRethrows(true);
        TS_ASSERT_THROWS_NOTHING(alg.initialize())
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputWS))
        TS_ASSERT_THROWS_NOTHING(
            alg.setPropertyValue("InputWorkspaceIndexSet", std::to_string(i)))
        TS_ASSERT_THROWS_NOTHING(
            alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
        TS_ASSERT_THROWS_NOTHING(
            alg.setProperty("BeamCentre", static_cast<int>(i)))
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("FlatSample", isFlatSample))
        TS_ASSERT_THROWS_NOTHING(alg.setProperty("IncludePartialBins", false))
        TS_ASSERT_THROWS_NOTHING(alg.execute())
        API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace");
        TS_ASSERT(outputWS);
        TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1)
        auto hist = outputWS->histogram(0);
        const auto firstItem = *hist.begin();
        for (const auto &i : hist) {
          TS_ASSERT_DELTA(i.binWidth(), firstItem.binWidth(), 1e-12)
          TS_ASSERT_DELTA(i.counts(), firstItem.counts(), 1e-1)
          TS_ASSERT_DELTA(i.countStandardDeviation(),
                          firstItem.countStandardDeviation(), 1e-1)
  void test_isTwoThetaSignAgnostic() {
    using namespace Mantid;
    auto inputWS = testWorkspace(0, 51); // One spectrum is monitor
    inputWS = detectorsOnly(inputWS);
    inputWS = convertToWavelength(inputWS);
    const auto &spectrumInfo = inputWS->spectrumInfo();
    constexpr int spectrum1{1};
    const int spectrum2{static_cast<int>(spectrumInfo.size()) - 2};
    TS_ASSERT_LESS_THAN(spectrumInfo.signedTwoTheta(spectrum1), 0.)
    TS_ASSERT_LESS_THAN(0., spectrumInfo.signedTwoTheta(spectrum2))
    double summedInLambda{0.};
    for (auto i : std::array<int, 2>{{spectrum1, spectrum2}}) {
      const auto &Ys = inputWS->y(i);
      summedInLambda += std::accumulate(Ys.cbegin(), Ys.cend(), 0.0);
    }
    std::ostringstream indexSetValue;
    indexSetValue << spectrum1 << "," << spectrum2;
    const std::array<bool, 2> flatSampleOptions{{true, false}};
    for (const auto isFlatSample : flatSampleOptions) {
      ReflectometrySumInQ alg;
      alg.setChild(true);
      alg.setRethrows(true);
      TS_ASSERT_THROWS_NOTHING(alg.initialize())
      TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputWS))
      TS_ASSERT_THROWS_NOTHING(
          alg.setPropertyValue("InputWorkspaceIndexSet", indexSetValue.str()))
      TS_ASSERT_THROWS_NOTHING(
          alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
      TS_ASSERT_THROWS_NOTHING(alg.setProperty("BeamCentre", spectrum1))
      TS_ASSERT_THROWS_NOTHING(alg.setProperty("FlatSample", isFlatSample))
      TS_ASSERT_THROWS_NOTHING(alg.setProperty("IncludePartialBins", true))
      TS_ASSERT_THROWS_NOTHING(alg.execute())
      API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace");
      TS_ASSERT(outputWS);
      TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1)
      auto &Ys = outputWS->y(0);
      const auto summedInQ = std::accumulate(Ys.cbegin(), Ys.cend(), 0.0);
      TS_ASSERT_DELTA(summedInQ, summedInLambda, 1e-10)
  void test_monitorNextToDetectorsThrows() {
    auto inputWS = testWorkspace();
    inputWS = convertToWavelength(inputWS);
    ReflectometrySumInQ alg;
    alg.setChild(true);
    alg.setRethrows(true);
    constexpr size_t monitorIdx{0};
    constexpr size_t detectorIdx{1};
    TS_ASSERT(inputWS->spectrumInfo().isMonitor(monitorIdx))
    TS_ASSERT(!inputWS->spectrumInfo().isMonitor(detectorIdx))
    TS_ASSERT_THROWS_NOTHING(alg.initialize())
    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputWS))
    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaceIndexSet",
                                                  std::to_string(detectorIdx)))
    TS_ASSERT_THROWS_NOTHING(
        alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
    TS_ASSERT_THROWS_NOTHING(
        alg.setProperty("BeamCentre", static_cast<int>(detectorIdx)))
    TS_ASSERT_THROWS_NOTHING(alg.setProperty("FlatSample", true))
    TS_ASSERT_THROWS_EQUALS(alg.execute(), const std::runtime_error &e,
                            e.what(),
                            std::string("Some invalid Properties found"))
  }

  void test_monitorInIndexSetThrows() {
    auto inputWS = testWorkspace();
    inputWS = convertToWavelength(inputWS);
    ReflectometrySumInQ alg;
    alg.setChild(true);
    alg.setRethrows(true);
    const size_t monitorIdx{0};
    TS_ASSERT(inputWS->spectrumInfo().isMonitor(monitorIdx))
    TS_ASSERT_THROWS_NOTHING(alg.initialize())
    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputWS))
    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaceIndexSet",
                                                  std::to_string(monitorIdx)))
    TS_ASSERT_THROWS_NOTHING(
        alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
    TS_ASSERT_THROWS_NOTHING(
        alg.setProperty("BeamCentre", static_cast<int>(monitorIdx)))
    TS_ASSERT_THROWS_NOTHING(alg.setProperty("FlatSample", true))
    TS_ASSERT_THROWS_EQUALS(alg.execute(), const std::runtime_error &e,
                            e.what(),
                            std::string("Some invalid Properties found"))
  }

  void test_BeamCentreNotInIndexSetThrows() {
    auto inputWS = testWorkspace();
    inputWS = convertToWavelength(inputWS);
    inputWS = detectorsOnly(inputWS);
    ReflectometrySumInQ alg;
    alg.setChild(true);
    alg.setRethrows(true);
    TS_ASSERT_THROWS_NOTHING(alg.initialize())
    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", inputWS))
    TS_ASSERT_THROWS_NOTHING(
        alg.setPropertyValue("InputWorkspaceIndexSet", "0, 1"))
    TS_ASSERT_THROWS_NOTHING(
        alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
    TS_ASSERT_THROWS_NOTHING(alg.setProperty("BeamCentre", 2))
    TS_ASSERT_THROWS_NOTHING(alg.setProperty("FlatSample", true))
    TS_ASSERT_THROWS_EQUALS(alg.execute(), const std::runtime_error &e,
                            e.what(),
                            std::string("Some invalid Properties found"))
  static Mantid::API::MatrixWorkspace_sptr
  testWorkspace(const double centreTwoThetaDegrees = 0.87,
                const int nSpectra = 4) {
    using namespace Mantid;
    using namespace WorkspaceCreationHelper;
    constexpr double startX{0.1};
    const Kernel::V3D slit1Pos{0., 0., -2.};
    const Kernel::V3D slit2Pos{0., 0., -1.};
    constexpr double vg1{0.5};
    constexpr double vg2{1.};
    const Kernel::V3D sourcePos{0., 0., -50.};
    const Kernel::V3D monitorPos{0., 0., -0.5};
    const Kernel::V3D samplePos{
    const double twoTheta{centreTwoThetaDegrees / 180. * M_PI};
    constexpr double detectorHeight{0.001};
    constexpr double l2{2.3};
    const auto y = l2 * std::sin(twoTheta);
    const auto z = l2 * std::cos(twoTheta);
    const Kernel::V3D centrePos{0., y, z};
    constexpr int nBins{50};
    return create2DWorkspaceWithReflectometryInstrumentMultiDetector(
        startX, detectorHeight, slit1Pos, slit2Pos, vg1, vg2, sourcePos,
        monitorPos, samplePos, centrePos, nSpectra, nBins);
class ReflectometrySumInQTestPerformance : public CxxTest::TestSuite {
public:
  static ReflectometrySumInQTestPerformance *createSuite() {
    return new ReflectometrySumInQTestPerformance();
  }
  static void destroySuite(ReflectometrySumInQTestPerformance *suite) {
    delete suite;
  }

  ReflectometrySumInQTestPerformance() {
    using namespace Mantid;
    using namespace WorkspaceCreationHelper;
    constexpr double startX{0.};
    const Kernel::V3D slit1Pos{0., 0., -2.};
    const Kernel::V3D slit2Pos{0., 0., -1.};
    constexpr double vg1{0.5};
    constexpr double vg2{1.};
    const Kernel::V3D sourcePos{0., 0., -50.};
    const Kernel::V3D monitorPos{0., 0., -0.5};
    const Kernel::V3D samplePos{
    constexpr double twoTheta{5.87 / 180. * M_PI};
    constexpr double detectorHeight{0.001};
    constexpr double l2{2.3};
    const auto y = l2 * std::sin(twoTheta);
    const auto z = l2 * std::cos(twoTheta);
    const Kernel::V3D centrePos{0., y, z};
    constexpr int nSpectra{101}; // One spectrum is monitor
    constexpr int nBins{200};
    constexpr double binWidth{1250.};
    m_workspace = create2DWorkspaceWithReflectometryInstrumentMultiDetector(
        startX, detectorHeight, slit1Pos, slit2Pos, vg1, vg2, sourcePos,
        monitorPos, samplePos, centrePos, nSpectra, nBins, binWidth);
    m_workspace = ReflectometrySumInQTest::convertToWavelength(m_workspace);
    m_workspace = ReflectometrySumInQTest::detectorsOnly(m_workspace);
    m_fullIndexSet.assign(m_workspace->getNumberHistograms(), 0);
    std::iota(m_fullIndexSet.begin(), m_fullIndexSet.end(), 0);
  }

  void test_typical() {
    ReflectometrySumInQ alg;
    alg.setChild(true);
    alg.setRethrows(true);
    alg.initialize();
    alg.setProperty("InputWorkspace", m_workspace);
    alg.setProperty("InputWorkspaceIndexSet", m_fullIndexSet);
    alg.setPropertyValue("OutputWorkspace", "_unused_for_child");
    alg.setProperty("BeamCentre", 49);
    alg.setProperty("FlatSample", true);
    for (int repetitions = 0; repetitions < 1000; ++repetitions) {
      alg.execute();
    }
  }

private:
  Mantid::API::MatrixWorkspace_sptr m_workspace;
  std::vector<int64_t> m_fullIndexSet;
};

#endif /* MANTID_ALGORITHMS_REFLECTOMETRYSUMINQTEST_H_ */