Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
IndirectFitDataTest.h 20.03 KiB
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#pragma once

#include <cxxtest/TestSuite.h>

#include "IndirectFitData.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidTestHelpers/IndirectFitDataCreationHelper.h"
#include "MantidTestHelpers/WorkspaceCreationHelper.h"

using namespace Mantid::API;
using namespace Mantid::DataObjects;
using namespace MantidQt::CustomInterfaces::IDA;
using namespace Mantid::IndirectFitDataCreationHelper;
using namespace MantidQt::CustomInterfaces;

namespace {

std::unique_ptr<IndirectFitData>
getIndirectFitData(int const &numberOfSpectra) {
  auto const workspace = createWorkspace(numberOfSpectra);
  Spectra const spec =
      Spectra(MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0},
              MantidQt::CustomInterfaces::IDA::WorkspaceIndex{
                  static_cast<int>(workspace->getNumberHistograms() - 1)});
  IndirectFitData data(workspace, spec);
  return std::make_unique<IndirectFitData>(data);
}

} // namespace

class IndirectFitDataTest : public CxxTest::TestSuite {
public:
  static IndirectFitDataTest *createSuite() {
    return new IndirectFitDataTest();
  }

  static void destroySuite(IndirectFitDataTest *suite) { delete suite; }

  void tearDown() override { AnalysisDataService::Instance().clear(); }

  void test_data_is_instantiated() {
    auto const workspace = createWorkspace(1);
    Spectra const spec =
        Spectra(MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0},
                MantidQt::CustomInterfaces::IDA::WorkspaceIndex{
                    static_cast<int>(workspace->getNumberHistograms() - 1)});

    workspace->setTitle("Test Title");
    IndirectFitData const data(workspace, spec);

    TS_ASSERT_EQUALS(data.workspace(), workspace);
    TS_ASSERT_EQUALS(data.workspace()->getTitle(), "Test Title");
    TS_ASSERT_EQUALS(data.workspace()->getNumberHistograms(), 1);
  }

  void test_that_DiscontinuousSpectra_is_set_up_correctly() {
    Spectra const spectra = Spectra("0-5,8,10");

    std::string const spectraString = "0-5,8,10";
    std::vector<IDA::WorkspaceIndex> const spectraVec{
        IDA::WorkspaceIndex(0), IDA::WorkspaceIndex(1), IDA::WorkspaceIndex(2),
        IDA::WorkspaceIndex(3), IDA::WorkspaceIndex(4), IDA::WorkspaceIndex(5),
        IDA::WorkspaceIndex(8), IDA::WorkspaceIndex(10)};

    TS_ASSERT_EQUALS(spectra.getString(), spectraString);
    for (auto it = spectra.begin(); it < spectra.end(); ++it)
      TS_ASSERT_EQUALS(*it, spectraVec[it - spectra.begin()]);
  }

  void
  test_that_DiscontinuousSpectra_is_sorted_before_being_stored_when_the_input_string_contains_overlapping_spectra() {
    auto data = getIndirectFitData(11);

    std::string const inputString = "8,0-7,6,10";
    Spectra const spectra = Spectra("0-8,10");
    data->setSpectra(inputString);

    TS_ASSERT_EQUALS(data->spectra(), spectra);
  }

  void
  test_that_DiscontinuousSpectra_is_sorted_before_being_stored_when_the_input_string_contains_an_invalid_spectra_range() {
    auto data = getIndirectFitData(11);

    std::string const inputString = "1,2,4-3,10";
    Spectra const spectra = Spectra("1-4,10");
    data->setSpectra(inputString);

    TS_ASSERT_EQUALS(data->spectra(), spectra);
  }

  void
  test_that_DiscontinuousSpectra_is_sorted_before_being_stored_when_the_input_string_contains_large_spaces() {
    auto data = getIndirectFitData(11);

    std::string const inputString = "  8,10,  7";
    Spectra const spectra = Spectra("7-8,10");
    data->setSpectra(inputString);

    TS_ASSERT_EQUALS(data->spectra(), spectra);
  }

  void test_data_is_stored_in_the_ADS() {
    auto const data = getIndirectFitData(1);
    SetUpADSWithWorkspace ads("WorkspaceName", data->workspace());

    TS_ASSERT(ads.doesExist("WorkspaceName"));
    auto workspace = ads.retrieveWorkspace("WorkspaceName");
    TS_ASSERT_EQUALS(workspace->getNumberHistograms(), 1);
  }

  void
  test_displayName_returns_a_valid_name_when_provided_a_rangeDelimiter_and_spectrum_number() {
    auto const data = getIndirectFitData(1);

    std::vector<std::string> const formatStrings{
        "%1%_s%2%_Result", "%1%_f%2%,s%2%_Parameter", "%1%_s%2%_Parameter"};
    std::string const rangeDelimiter = "_to_";
    IDA::WorkspaceIndex const spectrum = IDA::WorkspaceIndex{1};

    TS_ASSERT_EQUALS(data->displayName(formatStrings[0], rangeDelimiter),
                     "_s0_Result");
    TS_ASSERT_EQUALS(data->displayName(formatStrings[1], rangeDelimiter),
                     "_f0+s0_Parameter");
    TS_ASSERT_EQUALS(data->displayName(formatStrings[2], spectrum),
                     "_s1_Parameter");
  }

  void test_displayName_removes_red_part_of_a_workspace_name() {
    auto const data = getIndirectFitData(1);

    SetUpADSWithWorkspace ads("Workspace_3456_red", data->workspace());
    std::string const formatString = "%1%_s%2%_Result";
    std::string const rangeDelimiter = "_to_";

    TS_ASSERT_EQUALS(data->displayName(formatString, rangeDelimiter),
                     "Workspace_3456_s0_Result");
  }

  void
  test_that_the_number_of_spectra_returned_matches_the_instantiated_value() {
    auto const data = getIndirectFitData(10);
    TS_ASSERT_EQUALS(data->numberOfSpectra(), TableRowIndex{10});
  }

  void test_that_getSpectrum_returns_the_expected_spectrum_numbers() {
    auto const data = getIndirectFitData(4);

    for (auto i = TableRowIndex{0}; i < data->numberOfSpectra(); ++i) {
      MantidQt::CustomInterfaces::IDA::WorkspaceIndex const spectrumNum =
          data->getSpectrum(i);
      TS_ASSERT_EQUALS(spectrumNum.value, i.value);
    }
  }

  void
  test_that_true_is_returned_from_zeroSpectra_if_data_contains_empty_workspace() {
    auto workspace = std::make_shared<Workspace2D>();
    Spectra const spec = Spectra("");
    IndirectFitData const data(workspace, spec);

    TS_ASSERT_EQUALS(data.zeroSpectra(), true);
  }

  void
  test_that_true_is_returned_from_zeroSpectra_if_data_contains_empty_spectra() {
    auto const workspace = createWorkspace(1);
    Spectra const spec("");
    IndirectFitData const data(workspace, spec);

    TS_ASSERT_EQUALS(data.zeroSpectra(), true);
  }

  void
  test_that_false_is_returned_from_zeroSpectra_if_data_contains_one_or_more_spectra() {
    for (auto i = 1u; i < 10; ++i) {
      auto const data = getIndirectFitData(i);
      TS_ASSERT_EQUALS(data->zeroSpectra(), false);
    }
  }

  void
  test_that_correct_excludeRegion_is_returned_when_regions_are_in_correct_order() {
    /// When each pair of numbers in the string are in order, then the whole
    /// string is in the correct order(unordered: 10,11 9,7
    /// ordered:10,11,7,9)
    auto data = getIndirectFitData(4);

    data->setExcludeRegionString(
        "1,8", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0});
    data->setExcludeRegionString(
        "2,5", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1});
    data->setExcludeRegionString(
        "1,2,5,6,3,4", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{2});

    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0}),
                     "1.000,8.000");
    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1}),
                     "2.000,5.000");
    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{2}),
                     "1.000,2.000,5.000,6.000,3.000,4.000");
    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{3}),
                     "");
  }

  void
  test_that_correct_excludeRegionVector_is_returned_when_regions_are_in_correct_order() {
    /// When each pair of numbers in the string are in order, then the whole
    /// string is in the correct order(unordered: 10,11 9,7
    /// ordered:10,11,7,9)
    auto data = getIndirectFitData(4);

    data->setExcludeRegionString(
        "1,8", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0});
    data->setExcludeRegionString(
        "2,5", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1});
    std::vector<double> const regionVector1{1.0, 8.0};
    std::vector<double> const regionVector2{2.0, 5.0};

    TS_ASSERT_EQUALS(data->excludeRegionsVector(IDA::WorkspaceIndex{0}),
                     regionVector1);
    TS_ASSERT_EQUALS(data->excludeRegionsVector(IDA::WorkspaceIndex{1}),
                     regionVector2);
    TS_ASSERT_EQUALS(data->excludeRegionsVector(IDA::WorkspaceIndex{3}).empty(),
                     true);
  }

  void test_that_excludeRegion_pairs_are_stored_in_an_order_of_low_to_high() {
    /// Example: unordered: 10,11 9,7     ordered: 10,11,7,9
    auto data = getIndirectFitData(3);

    data->setExcludeRegionString(
        "6,2", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0});
    data->setExcludeRegionString(
        "6,2,1,2,3,4,7,6", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1});
    data->setExcludeRegionString(
        "1,2,2,3,20,18,21,22,7,8",
        MantidQt::CustomInterfaces::IDA::WorkspaceIndex{2});

    std::vector<double> const regionVector{2.0, 6.0};

    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0}),
                     "2.000,6.000");
    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1}),
                     "2.000,6.000,1.000,2.000,3.000,4.000,6.000,7.000");
    TS_ASSERT_EQUALS(
        data->getExcludeRegion(
            MantidQt::CustomInterfaces::IDA::WorkspaceIndex{2}),
        "1.000,2.000,2.000,3.000,18.000,20.000,21.000,22.000,7.000,8.000");
    TS_ASSERT_EQUALS(data->excludeRegionsVector(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0}),
                     regionVector);
  }

  void
  test_that_excludeRegion_is_stored_without_spaces_when_there_are_many_spaces_in_input_string() {
    auto data = getIndirectFitData(3);

    data->setExcludeRegionString(
        "  6,     2", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0});
    data->setExcludeRegionString(
        "6,  2,1  ,2,  3,4  ,7,6",
        MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1});
    data->setExcludeRegionString(
        "1,2 ,2,3,  20,  18,21,22,7, 8   ",
        MantidQt::CustomInterfaces::IDA::WorkspaceIndex{2});
    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0}),
                     "2.000,6.000");
    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1}),
                     "2.000,6.000,1.000,2.000,3.000,4.000,6.000,7.000");
    TS_ASSERT_EQUALS(
        data->getExcludeRegion(
            MantidQt::CustomInterfaces::IDA::WorkspaceIndex{2}),
        "1.000,2.000,2.000,3.000,18.000,20.000,21.000,22.000,7.000,8.000");
  }

  void
  test_that_setExcludeRegion_rounds_the_numbers_in_the_input_string_to_the_appropriate_decimal_place() {
    auto data = getIndirectFitData(2);

    data->setExcludeRegionString(
        "6.29445,2.93343", MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0});
    data->setExcludeRegionString(
        "2.6,2.3,1.9999,3.0125",
        MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1});

    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0}),
                     "2.933,6.294");
    TS_ASSERT_EQUALS(data->getExcludeRegion(
                         MantidQt::CustomInterfaces::IDA::WorkspaceIndex{1}),
                     "2.300,2.600,2.000,3.013");
  }

  void test_throws_when_setSpectra_is_provided_an_out_of_range_spectra() {
    auto data = getIndirectFitData(10);

    std::vector<Spectra> const spectraPairs{
        Spectra(MantidQt::CustomInterfaces::IDA::WorkspaceIndex{0},
                MantidQt::CustomInterfaces::IDA::WorkspaceIndex{11}),
        Spectra(MantidQt::CustomInterfaces::IDA::WorkspaceIndex{10},
                MantidQt::CustomInterfaces::IDA::WorkspaceIndex{10})};
    std::vector<std::string> const spectraStrings{"10", "1,5,10",
                                                  "1,2,3,4,5,6,22"};

    for (auto i = 0u; i < spectraPairs.size(); ++i)
      TS_ASSERT_THROWS(data->setSpectra(spectraPairs[i]),
                       const std::runtime_error &);
    for (auto i = 0u; i < spectraStrings.size(); ++i)
      TS_ASSERT_THROWS(data->setSpectra(spectraStrings[i]),
                       const std::runtime_error &);
  }

  void test_no_throw_when_setSpectra_is_provided_a_valid_spectra() {
    auto data = getIndirectFitData(10);

    std::vector<Spectra> const spectraPairs{
        Spectra(IDA::WorkspaceIndex{0}, IDA::WorkspaceIndex{9}),
        Spectra(IDA::WorkspaceIndex{4}, IDA::WorkspaceIndex{4}),
        Spectra(IDA::WorkspaceIndex{7}, IDA::WorkspaceIndex{4})};
    std::vector<std::string> const spectraStrings{"0", "9", "0,9,6,4,5",
                                                  "1,2,3,4,5,6"};

    for (auto i = 0u; i < spectraPairs.size(); ++i)
      TS_ASSERT_THROWS_NOTHING(data->setSpectra(spectraPairs[i]));
    for (auto i = 0u; i < spectraStrings.size(); ++i)
      TS_ASSERT_THROWS_NOTHING(data->setSpectra(spectraStrings[i]));
  }

  void
  test_no_throw_when_setStartX_is_provided_a_valid_xValue_and_spectrum_number() {
    auto data = getIndirectFitData(10);

    TS_ASSERT_THROWS_NOTHING(data->setStartX(0.0, IDA::WorkspaceIndex{0}));
    TS_ASSERT_THROWS_NOTHING(data->setStartX(-5.0, IDA::WorkspaceIndex{0}));
    TS_ASSERT_THROWS_NOTHING(data->setStartX(5000000, IDA::WorkspaceIndex{10}));
  }

  void test_the_provided_startX_is_stored_in_range_after_using_setStartX() {
    auto data = getIndirectFitData(3);

    data->setStartX(-5.0, IDA::WorkspaceIndex{0});
    data->setStartX(6.53, IDA::WorkspaceIndex{1});
    data->setStartX(100000000000000.0, IDA::WorkspaceIndex{2});

    TS_ASSERT_EQUALS(data->getRange(IDA::WorkspaceIndex{0}).first, -5.0);
    TS_ASSERT_EQUALS(data->getRange(IDA::WorkspaceIndex{1}).first, 6.53);
    TS_ASSERT_EQUALS(data->getRange(IDA::WorkspaceIndex{2}).first,
                     100000000000000.0);
  }

  void
  test_no_throw_when_setEndX_is_provided_a_valid_xValue_and_spectrum_number() {
    auto data = getIndirectFitData(10);

    TS_ASSERT_THROWS_NOTHING(data->setStartX(0.0, IDA::WorkspaceIndex{0}));
    TS_ASSERT_THROWS_NOTHING(data->setStartX(-5.0, IDA::WorkspaceIndex{0}));
    TS_ASSERT_THROWS_NOTHING(data->setStartX(5000000, IDA::WorkspaceIndex{10}));
  }

  void test_the_provided_endX_is_stored_in_range_after_using_setEndX() {
    auto data = getIndirectFitData(3);

    data->setEndX(-5.0, IDA::WorkspaceIndex{0});
    data->setEndX(6.53, IDA::WorkspaceIndex{1});
    data->setEndX(100000000000000.0, IDA::WorkspaceIndex{2});

    TS_ASSERT_EQUALS(data->getRange(IDA::WorkspaceIndex{0}).second, -5.0);
    TS_ASSERT_EQUALS(data->getRange(IDA::WorkspaceIndex{1}).second, 6.53);
    TS_ASSERT_EQUALS(data->getRange(IDA::WorkspaceIndex{2}).second,
                     100000000000000.0);
  }

  void
  test_that_the_startX_of_two_data_sets_are_combined_when_relating_to_two_seperate_spectra() {
    auto data1 = getIndirectFitData(2);
    auto data2 = getIndirectFitData(2);

    data1->setStartX(6.53, IDA::WorkspaceIndex{0});
    data2->setStartX(5.0, IDA::WorkspaceIndex{1});
    auto const combinedData = data2->combine(*data1);

    TS_ASSERT_EQUALS(combinedData.getRange(IDA::WorkspaceIndex{0}).first, 6.53);
    TS_ASSERT_EQUALS(combinedData.getRange(IDA::WorkspaceIndex{1}).first, 5.0);
  }

  void
  test_that_the_endX_of_two_datasets_are_combined_when_relating_to_two_seperate_spectra() {
    auto data1 = getIndirectFitData(2);
    auto data2 = getIndirectFitData(2);

    data1->setEndX(2.34, IDA::WorkspaceIndex{0});
    data2->setEndX(5.9, IDA::WorkspaceIndex{1});
    auto const combinedData = data2->combine(*data1);

    TS_ASSERT_EQUALS(combinedData.getRange(IDA::WorkspaceIndex{0}).second,
                     2.34);
    TS_ASSERT_EQUALS(combinedData.getRange(IDA::WorkspaceIndex{1}).second, 5.9);
  }

  void
  test_that_the_excludeRegion_of_two_datasets_are_combined_when_relating_to_two_seperate_spectra() {
    auto data1 = getIndirectFitData(2);
    auto data2 = getIndirectFitData(2);

    data1->setExcludeRegionString("1,2,6,5", IDA::WorkspaceIndex{0});
    data1->setExcludeRegionString("6,2", IDA::WorkspaceIndex{1});
    auto const combinedData = data2->combine(*data1);

    TS_ASSERT_EQUALS(combinedData.getExcludeRegion(IDA::WorkspaceIndex{0}),
                     std::string("1.000,2.000,5.000,6.000"));
    TS_ASSERT_EQUALS(combinedData.getExcludeRegion(IDA::WorkspaceIndex{1}),
                     std::string("2.000,6.000"));
  }

  void
  test_that_the_spectra_pair_of_two_datasets_are_combined_correctly_when_spectra_do_not_overlap() {
    auto data1 = getIndirectFitData(10);
    auto data2 = getIndirectFitData(10);

    data1->setSpectra(Spectra(IDA::WorkspaceIndex{0}, IDA::WorkspaceIndex{4}));
    data2->setSpectra(Spectra(IDA::WorkspaceIndex{5}, IDA::WorkspaceIndex{9}));
    auto const combinedData = data2->combine(*data1);
    Spectra const spec(Spectra(IDA::WorkspaceIndex{0}, IDA::WorkspaceIndex{9}));

    TS_ASSERT_EQUALS(combinedData.spectra(), spec);
  }

  void
  test_that_the_spectra_pair_of_two_datasets_are_combined_correctly_when_spectra_are_discontinuous() {
    auto data1 = getIndirectFitData(10);
    auto data2 = getIndirectFitData(10);

    data1->setSpectra(Spectra(IDA::WorkspaceIndex{0}, IDA::WorkspaceIndex{4}));
    data2->setSpectra(Spectra(IDA::WorkspaceIndex{8}, IDA::WorkspaceIndex{9}));
    auto const combinedData = data2->combine(*data1);
    Spectra const spec(Spectra("0-4,8-9"));

    TS_ASSERT_EQUALS(combinedData.spectra(), spec);
  }

  void
  test_that_the_spectra_pair_of_two_datasets_are_combined_correctly_when_spectra_overlap() {
    auto data1 = getIndirectFitData(10);
    auto data2 = getIndirectFitData(10);

    data1->setSpectra(Spectra(IDA::WorkspaceIndex{0}, IDA::WorkspaceIndex{8}));
    data2->setSpectra(Spectra(IDA::WorkspaceIndex{4}, IDA::WorkspaceIndex{9}));
    auto const combinedData = data2->combine(*data1);
    Spectra const spec(Spectra("0-9"));

    TS_ASSERT_EQUALS(combinedData.spectra(), spec);
  }

  void
  test_that_the_DiscontinuousSpectra_of_two_datasets_are_combined_correctly_when_spectra_do_not_overlap() {
    auto data1 = getIndirectFitData(10);
    auto data2 = getIndirectFitData(10);

    data1->setSpectra(Spectra("0-4"));
    data2->setSpectra(Spectra("5-9"));
    auto const combinedData = data2->combine(*data1);
    Spectra const spec(Spectra("0-9"));

    TS_ASSERT_EQUALS(combinedData.spectra(), spec);
  }

  void
  test_that_the_DiscontinuousSpectra_of_two_datasets_are_combined_correctly_when_spectra_overlap() {
    auto data1 = getIndirectFitData(10);
    auto data2 = getIndirectFitData(10);

    data1->setSpectra(Spectra("0-7"));
    data2->setSpectra(Spectra("2-9"));
    auto const combinedData = data2->combine(*data1);
    Spectra const spec(Spectra("0-9"));

    TS_ASSERT_EQUALS(combinedData.spectra(), spec);
  }

  void
  test_that_a_Spectra_pair_and_DiscontinuousSpectra_dataset_are_combined_correctly_when_spectra_do_not_overlap() {
    auto data1 = getIndirectFitData(10);
    auto data2 = getIndirectFitData(10);

    data1->setSpectra(Spectra("0-4"));
    data2->setSpectra(Spectra(IDA::WorkspaceIndex{5}, IDA::WorkspaceIndex{9}));
    auto const combinedData = data2->combine(*data1);
    Spectra const spec(Spectra("0-9"));

    TS_ASSERT_EQUALS(combinedData.spectra(), spec);
  }

  void
  test_that_a_Spectra_pair_and_DiscontinuousSpectra_dataset_are_combined_correctly_when_spectra_overlap() {
    auto data1 = getIndirectFitData(10);
    auto data2 = getIndirectFitData(10);

    data1->setSpectra(Spectra("0-7"));
    data2->setSpectra(Spectra(IDA::WorkspaceIndex{4}, IDA::WorkspaceIndex{9}));
    auto const combinedData = data2->combine(*data1);
    Spectra const spec(Spectra("0-9"));

    TS_ASSERT_EQUALS(combinedData.spectra(), spec);
  }
};