diff --git a/Framework/Muon/CMakeLists.txt b/Framework/Muon/CMakeLists.txt index 7d495a27ea01718a488e5f5a80c767afe4c3ce5e..676a0670d395d59b5c1a59e01a1ecf7b4f54a373 100644 --- a/Framework/Muon/CMakeLists.txt +++ b/Framework/Muon/CMakeLists.txt @@ -1,66 +1,69 @@ set ( SRC_FILES - src/AlphaCalc.cpp - src/ApplyDeadTimeCorr.cpp + src/AlphaCalc.cpp + src/ApplyDeadTimeCorr.cpp src/ApplyMuonDetectorGrouping.cpp src/ApplyMuonDetectorGroupPairing.cpp - src/AsymmetryCalc.cpp - src/CalculateMuonAsymmetry.cpp - src/CalMuonDeadTime.cpp - src/CalMuonDetectorPhases.cpp + src/AsymmetryCalc.cpp + src/CalculateMuonAsymmetry.cpp + src/CalMuonDeadTime.cpp + src/CalMuonDetectorPhases.cpp src/ConvertFitFunctionForMuonTFAsymmetry.cpp src/LoadAndApplyMuonDetectorGrouping.cpp - src/EstimateMuonAsymmetryFromCounts.cpp + src/EstimateMuonAsymmetryFromCounts.cpp src/MuonAlgorithmHelper.cpp - src/MuonAsymmetryHelper.cpp - src/MuonGroupDetectors.cpp - src/MuonPreProcess.cpp - src/PhaseQuadMuon.cpp - src/PlotAsymmetryByLogValue.cpp - src/RemoveExpDecay.cpp - src/RRFMuon.cpp + src/MuonAsymmetryHelper.cpp + src/MuonGroupDetectors.cpp + src/MuonGroupingCounts.cpp + src/MuonPreProcess.cpp + src/PhaseQuadMuon.cpp + src/PlotAsymmetryByLogValue.cpp + src/RemoveExpDecay.cpp + src/RRFMuon.cpp ) set ( INC_FILES - inc/MantidMuon/AlphaCalc.h - inc/MantidMuon/ApplyDeadTimeCorr.h + inc/MantidMuon/AlphaCalc.h + inc/MantidMuon/ApplyDeadTimeCorr.h inc/MantidMuon/ApplyMuonDetectorGrouping.h inc/MantidMuon/ApplyMuonDetectorGroupPairing.h - inc/MantidMuon/AsymmetryCalc.h - inc/MantidMuon/CalculateMuonAsymmetry.h - inc/MantidMuon/CalMuonDeadTime.h - inc/MantidMuon/CalMuonDetectorPhases.h + inc/MantidMuon/AsymmetryCalc.h + inc/MantidMuon/CalculateMuonAsymmetry.h + inc/MantidMuon/CalMuonDeadTime.h + inc/MantidMuon/CalMuonDetectorPhases.h inc/MantidMuon/ConvertFitFunctionForMuonTFAsymmetry.h inc/MantidMuon/LoadAndApplyMuonDetectorGrouping.h - inc/MantidMuon/EstimateMuonAsymmetryFromCounts.h + inc/MantidMuon/EstimateMuonAsymmetryFromCounts.h inc/MantidMuon/MuonAlgorithmHelper.h - inc/MantidMuon/MuonAsymmetryHelper.h - inc/MantidMuon/MuonGroupDetectors.h - inc/MantidMuon/MuonPreProcess.h - inc/MantidMuon/PhaseQuadMuon.h - inc/MantidMuon/PlotAsymmetryByLogValue.h - inc/MantidMuon/RemoveExpDecay.h - inc/MantidMuon/RRFMuon.h + inc/MantidMuon/MuonAsymmetryHelper.h + inc/MantidMuon/MuonGroupDetectors.h + inc/MantidMuon/MuonGroupingCounts.h + inc/MantidMuon/MuonPreProcess.h + inc/MantidMuon/PhaseQuadMuon.h + inc/MantidMuon/PlotAsymmetryByLogValue.h + inc/MantidMuon/RemoveExpDecay.h + inc/MantidMuon/RRFMuon.h ) set ( TEST_FILES - AlphaCalcTest.h - ApplyDeadTimeCorrTest.h + AlphaCalcTest.h + ApplyDeadTimeCorrTest.h ApplyMuonDetectorGroupingTest.h ApplyMuonDetectorGroupPairingTest.h - AsymmetryCalcTest.h - CalculateMuonAsymmetryTest.h - CalMuonDeadTimeTest.h - CalMuonDetectorPhasesTest.h + AsymmetryCalcTest.h + CalculateMuonAsymmetryTest.h + CalMuonDeadTimeTest.h + CalMuonDetectorPhasesTest.h ConvertFitFunctionForMuonTFAsymmetryTest.h LoadAndApplyMuonDetectorGroupingTest.h MuonAlgorithmHelperTest.h - EstimateMuonAsymmetryFromCountsTest.h - MuonGroupDetectorsTest.h - MuonPreProcessTest.h - PhaseQuadMuonTest.h - PlotAsymmetryByLogValueTest.h - RemoveExpDecayTest.h - RRFMuonTest.h + EstimateMuonAsymmetryFromCountsTest.h + MuonGroupDetectorsTest.h + MuonGroupingCountsTest.h + MuonPreProcessTest.h + PhaseQuadMuonTest.h + PlotAsymmetryByLogValueTest.h + RemoveExpDecayTest.h + RRFMuonTest.h ) if (COVERALLS) diff --git a/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h b/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h index f076ee23cd75da124d4c8c91fb821ed256744b33..eafd889e648203f4f57b1bc3034e6e814ebac68b 100644 --- a/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h +++ b/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h @@ -139,6 +139,8 @@ DLLExport void addSampleLog(Mantid::API::MatrixWorkspace_sptr workspace, const std::string &logName, const std::string &logValue); +DLLExport bool isAlphanumericOrUnderscore(char character); + // ///// Saves grouping to the XML file specified // DLLExport std::string groupingToXML(const Mantid::API::Grouping &grouping); diff --git a/Framework/Muon/inc/MantidMuon/MuonGroupingCounts.h b/Framework/Muon/inc/MantidMuon/MuonGroupingCounts.h new file mode 100644 index 0000000000000000000000000000000000000000..6ddcb46094c8489dd0ab5c64fa59308659f9c5ea --- /dev/null +++ b/Framework/Muon/inc/MantidMuon/MuonGroupingCounts.h @@ -0,0 +1,46 @@ +// 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 +// SPDX - License - Identifier: GPL - 3.0 + +#ifndef MANTID_MUON_MUONGROUPINGCOUNTS_H_ +#define MANTID_MUON_MUONGROUPINGCOUNTS_H_ + +#include "MantidAPI/Algorithm.h" + +using namespace Mantid::API; + +namespace Mantid { +namespace Muon { + +class DLLExport MuonGroupingCounts : public API::Algorithm { +public: + MuonGroupingCounts() : API::Algorithm() {} + ~MuonGroupingCounts() {} + + const std::string name() const override { return "MuonGroupingCounts"; } + int version() const override { return (1); } + const std::string category() const override { return "Muon\\DataHandling"; } + const std::string summary() const override { + return "Apply a grouping (summation of counts) across a set of detectors " + "in Muon data."; + } + const std::vector<std::string> seeAlso() const override { + return {"MuonProcess", "Minus", "Plus"}; + } + + /// Perform validation of inputs to the algorithm + std::map<std::string, std::string> validateInputs() override; + +private: + void init() override; + void exec() override; + + void setGroupingSampleLogs(MatrixWorkspace_sptr workspace); +}; + +} // namespace Muon +} // namespace Mantid + +#endif /* MANTID_MUON_MUONGROUPINGCOUNTS_H_ */ diff --git a/Framework/Muon/src/MuonAlgorithmHelper.cpp b/Framework/Muon/src/MuonAlgorithmHelper.cpp index 498a1e5fb1db175f0614e04a18ce6432da17f6ba..f6599d66788fae017c380482237417ea0915a9b3 100644 --- a/Framework/Muon/src/MuonAlgorithmHelper.cpp +++ b/Framework/Muon/src/MuonAlgorithmHelper.cpp @@ -607,5 +607,9 @@ void addSampleLog(MatrixWorkspace_sptr workspace, const std::string &logName, alg->execute(); } +bool isAlphanumericOrUnderscore(char character) { + return (isalpha(character) || isdigit(character) || (character == '_')); +} + } // namespace MuonAlgorithmHelper } // namespace Mantid diff --git a/Framework/Muon/src/MuonGroupingCounts.cpp b/Framework/Muon/src/MuonGroupingCounts.cpp new file mode 100644 index 0000000000000000000000000000000000000000..103e0234b434eb8e49a210a8e1d82ab4658f0278 --- /dev/null +++ b/Framework/Muon/src/MuonGroupingCounts.cpp @@ -0,0 +1,215 @@ +// 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 +// SPDX - License - Identifier: GPL - 3.0 + +#include "MantidMuon/MuonGroupingCounts.h" +#include "MantidAPI/Algorithm.h" +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidDataObjects/TableWorkspace.h" +#include "MantidHistogramData/HistogramMath.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/System.h" +#include "MantidMuon/MuonAlgorithmHelper.h" +#include <boost/format.hpp> + +using namespace Mantid::API; +using namespace Mantid::DataObjects; +using namespace Mantid::Kernel; +using namespace Mantid::HistogramData; + +namespace { + +bool checkPeriodInWorkspaceGroup(const int &period, + WorkspaceGroup_sptr workspace) { + return period <= workspace->getNumberOfEntries(); +} + +MatrixWorkspace_sptr groupDetectors(MatrixWorkspace_sptr workspace, + const std::vector<int> &detectorIDs) { + + auto outputWS = WorkspaceFactory::Instance().create(workspace, 1); + + std::vector<size_t> wsIndices = + workspace->getIndicesFromDetectorIDs(detectorIDs); + + if (wsIndices.size() != detectorIDs.size()) { + std::string errorMsg = + str(boost::format("The number of detectors" + "requested does not equalthe number of detectors " + "provided %1% != %2% ") % + wsIndices.size() % detectorIDs.size()); + throw std::invalid_argument(errorMsg); + } + + outputWS->getSpectrum(0).clearDetectorIDs(); + outputWS->setSharedX(0, workspace->sharedX(wsIndices.front())); + + auto hist = outputWS->histogram(0); + for (auto &wsIndex : wsIndices) { + hist += workspace->histogram(wsIndex); + outputWS->getSpectrum(0).addDetectorIDs( + workspace->getSpectrum(wsIndex).getDetectorIDs()); + } + outputWS->setHistogram(0, hist); + outputWS->getSpectrum(0).setSpectrumNo(static_cast<int32_t>(1)); + return outputWS; +} + +} // namespace + +namespace Mantid { +namespace Muon { + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(MuonGroupingCounts) + +void MuonGroupingCounts::init() { + std::string emptyString(""); + std::vector<int> defaultGrouping = {1}; + + declareProperty( + Mantid::Kernel::make_unique<WorkspaceProperty<WorkspaceGroup>>( + "InputWorkspace", emptyString, Direction::Input, + PropertyMode::Mandatory), + "Input workspace containing data from detectors which are to " + "be grouped."); + + declareProperty(Mantid::Kernel::make_unique<WorkspaceProperty<Workspace>>( + "OutputWorkspace", emptyString, Direction::Output), + "Output workspace which will hold the grouped data."); + + declareProperty("GroupName", emptyString, + "The name of the group. Must contain at least one " + "alphanumeric character.", + Direction::Input); + declareProperty(make_unique<ArrayProperty<int>>( + "Grouping", defaultGrouping, + IValidator_sptr(new NullValidator), Direction::Input), + "The grouping of detectors, comma separated list of detector " + "IDs or hyphenated ranges of IDs."); + + declareProperty(make_unique<ArrayProperty<int>>( + "SummedPeriods", defaultGrouping, + IValidator_sptr(new NullValidator), Direction::Input), + "A list of periods to sum in multiperiod data."); + declareProperty( + make_unique<ArrayProperty<int>>("SubtractedPeriods", Direction::Input), + "A list of periods to subtract in multiperiod data."); + + // Perform Group Associations. + + std::string groupingGrp("Grouping Information"); + setPropertyGroup("GroupName", groupingGrp); + setPropertyGroup("Grouping", groupingGrp); + + std::string periodGrp("Multi-period Data"); + setPropertyGroup("SummedPeriods", periodGrp); + setPropertyGroup("SubtractedPeriods", periodGrp); +} + +std::map<std::string, std::string> MuonGroupingCounts::validateInputs() { + std::map<std::string, std::string> errors; + + std::string groupName = this->getProperty("GroupName"); + if (groupName.empty()) { + errors["GroupName"] = "Group name must be specified."; + } + + if (!std::all_of(std::begin(groupName), std::end(groupName), + Mantid::MuonAlgorithmHelper::isAlphanumericOrUnderscore)) { + errors["GroupName"] = + "The group name must contain alphnumeric characters and _ only."; + } + + WorkspaceGroup_sptr inputWS = getProperty("InputWorkspace"); + std::vector<int> summedPeriods = getProperty("SummedPeriods"); + std::vector<int> subtractedPeriods = getProperty("SubtractedPeriods"); + + if (summedPeriods.empty() && subtractedPeriods.empty()) { + errors["SummedPeriods"] = "At least one period must be specified"; + } + + if (!summedPeriods.empty()) { + const int highestSummedPeriod = + *std::max_element(summedPeriods.begin(), summedPeriods.end()); + if (!checkPeriodInWorkspaceGroup(highestSummedPeriod, inputWS)) { + errors["SummedPeriods"] = "Requested period (" + + std::to_string(highestSummedPeriod) + + ") exceeds periods in data"; + } + if (std::any_of(summedPeriods.begin(), summedPeriods.end(), + [](const int &i) { return i < 0; })) { + errors["SummedPeriods"] = "Requested periods must be greater that 0."; + } + } + + if (!subtractedPeriods.empty()) { + const int highestSubtractedPeriod = + *std::max_element(subtractedPeriods.begin(), subtractedPeriods.end()); + if (!checkPeriodInWorkspaceGroup(highestSubtractedPeriod, inputWS)) { + errors["SubtractedPeriods"] = "Requested period (" + + std::to_string(highestSubtractedPeriod) + + ") exceeds periods in data"; + } + if (std::any_of(subtractedPeriods.begin(), subtractedPeriods.end(), + [](const int &i) { return i < 0; })) { + errors["SubtractedPeriods"] = "Requested periods must be greater that 0."; + } + } + + if (inputWS->getNumberOfEntries() < 1) { + errors["InputWorkspace"] = "WorkspaceGroup contains no periods."; + } + + return errors; +} + +void MuonGroupingCounts::exec() { + + WorkspaceGroup_sptr inputWS = getProperty("InputWorkspace"); + MatrixWorkspace_sptr outputWS; + + // Group detectors in each period + std::vector<int> group = getProperty("Grouping"); + auto groupedPeriods = boost::make_shared<WorkspaceGroup>(); + for (auto &&workspace : *inputWS) { + groupedPeriods->addWorkspace(groupDetectors( + boost::dynamic_pointer_cast<MatrixWorkspace>(workspace), group)); + } + + std::vector<int> summedPeriods = getProperty("SummedPeriods"); + std::vector<int> subtractedPeriods = getProperty("SubtractedPeriods"); + MatrixWorkspace_sptr addedPeriodsWS = + Mantid::MuonAlgorithmHelper::sumPeriods(groupedPeriods, summedPeriods); + if (!subtractedPeriods.empty()) { + MatrixWorkspace_sptr subtractedPeriodsWS = + Mantid::MuonAlgorithmHelper::sumPeriods(groupedPeriods, + subtractedPeriods); + outputWS = Mantid::MuonAlgorithmHelper::subtractWorkspaces( + addedPeriodsWS, subtractedPeriodsWS); + } else { + outputWS = addedPeriodsWS; + } + + setGroupingSampleLogs(outputWS); + setProperty("OutputWorkspace", outputWS); +} + +void MuonGroupingCounts::setGroupingSampleLogs(MatrixWorkspace_sptr workspace) { + MuonAlgorithmHelper::addSampleLog(workspace, "analysis_group_name", + getPropertyValue("GroupName")); + MuonAlgorithmHelper::addSampleLog(workspace, "analysis_group", + getPropertyValue("Grouping")); + MuonAlgorithmHelper::addSampleLog(workspace, "analysis_periods_summed", + getPropertyValue("SummedPeriods")); + MuonAlgorithmHelper::addSampleLog(workspace, "analysis_periods_subtracted", + getPropertyValue("SubtractedPeriods")); +} + +} // namespace Muon +} // namespace Mantid diff --git a/Framework/Muon/test/MuonGroupingCountsTest.h b/Framework/Muon/test/MuonGroupingCountsTest.h new file mode 100644 index 0000000000000000000000000000000000000000..2d3b2fc1d1308fdd52e9fb5b642f670d8ab94001 --- /dev/null +++ b/Framework/Muon/test/MuonGroupingCountsTest.h @@ -0,0 +1,322 @@ +// 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 +// SPDX - License - Identifier: GPL - 3.0 + +#ifndef MANTID_MUON_MUONGROUPINGCOUNTSTEST_H_ +#define MANTID_MUON_MUONGROUPINGCOUNTSTEST_H_ + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/FrameworkManager.h" +#include "MantidMuon/MuonGroupingCounts.h" +#include "MantidTestHelpers/MuonWorkspaceCreationHelper.h" + +#include <cxxtest/TestSuite.h> + +using namespace Mantid; +using namespace Mantid::Kernel; +using namespace Mantid::API; +using namespace Mantid::DataObjects; +using namespace Mantid::Muon; +using namespace MuonWorkspaceCreationHelper; + +namespace { + +// Simple class to set up the ADS with the configuration required by the +// algorithm (a MatrixWorkspace). +class setUpADSWithWorkspace { +public: + setUpADSWithWorkspace(Workspace_sptr ws) { + AnalysisDataService::Instance().addOrReplace(inputWSName, ws); + }; + ~setUpADSWithWorkspace() { AnalysisDataService::Instance().clear(); }; + + std::string const inputWSName = "inputData"; +}; + +// Set only mandatory fields; input and output workspace +IAlgorithm_sptr +algorithmWithoutOptionalPropertiesSet(const std::string &inputWSName) { + + auto alg = boost::make_shared<MuonGroupingCounts>(); + alg->initialize(); + alg->setProperty("InputWorkspace", inputWSName); + alg->setProperty("OutputWorkspace", "__notUsed"); + alg->setAlwaysStoreInADS(false); + alg->setLogging(false); + return alg; +} + +// Set up algorithm without any optional properties +// i.e. just the input workspace and group name. +IAlgorithm_sptr +setUpAlgorithmWithoutOptionalProperties(WorkspaceGroup_sptr ws, + const std::string &name) { + setUpADSWithWorkspace setup(ws); + IAlgorithm_sptr alg = + algorithmWithoutOptionalPropertiesSet(setup.inputWSName); + alg->setProperty("GroupName", name); + return alg; +} + +// Set up algorithm with GroupName applied +IAlgorithm_sptr setUpAlgorithmWithGroupName(WorkspaceGroup_sptr ws, + const std::string &name) { + setUpADSWithWorkspace setup(ws); + IAlgorithm_sptr alg = + algorithmWithoutOptionalPropertiesSet(setup.inputWSName); + alg->setProperty("GroupName", name); + return alg; +} + +// Set up algorithm with TimeOffset applied +IAlgorithm_sptr +setUpAlgorithmWithGroupNameAndDetectors(WorkspaceGroup_sptr ws, + const std::string &name, + const std::vector<int> &detectors) { + setUpADSWithWorkspace setup(ws); + IAlgorithm_sptr alg = + algorithmWithoutOptionalPropertiesSet(setup.inputWSName); + alg->setProperty("GroupName", name); + alg->setProperty("Grouping", detectors); + return alg; +} + +// Retrieve the output workspace from an executed algorithm +MatrixWorkspace_sptr getOutputWorkspace(IAlgorithm_sptr alg) { + Workspace_sptr outputWS = alg->getProperty("OutputWorkspace"); + auto wsOut = boost::dynamic_pointer_cast<MatrixWorkspace>(outputWS); + return wsOut; +} + +} // namespace + +class MuonGroupingCountsTest : public CxxTest::TestSuite { +public: + static MuonGroupingCountsTest *createSuite() { + return new MuonGroupingCountsTest(); + } + static void destroySuite(MuonGroupingCountsTest *suite) { delete suite; } + + // -------------------------------------------------------------------------- + // Initialization / Execution + // -------------------------------------------------------------------------- + + void test_that_algorithm_initializes() { + MuonGroupingCounts alg; + + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + } + + void test_that_algorithm_executes_with_no_optional_properties_set() { + + auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "group1"); + auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "group1"); + + TS_ASSERT_THROWS_NOTHING(alg->execute()); + TS_ASSERT(alg->isExecuted()) + } + + // -------------------------------------------------------------------------- + // Validation : Group Names and Detector Grouping + // -------------------------------------------------------------------------- + + void test_that_input_workspace_cannot_be_a_Workspace2D() { + + auto ws = createCountsWorkspace(5, 10, 0.0); + setUpADSWithWorkspace setup(ws); + auto alg = boost::make_shared<MuonGroupingCounts>(); + alg->initialize(); + + TS_ASSERT_THROWS_ANYTHING( + alg->setProperty("InputWorkspace", setup.inputWSName)); + } + + void test_that_input_workspace_can_be_a_WorkspaceGroup() { + + auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "group1"); + setUpADSWithWorkspace setup(ws); + auto alg = boost::make_shared<MuonGroupingCounts>(); + alg->initialize(); + + TSM_ASSERT_THROWS_NOTHING( + "", alg->setProperty("InputWorkspace", setup.inputWSName)) + } + + void test_that_group_name_must_be_supplied() { + + auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "group1"); + setUpADSWithWorkspace setup(ws); + IAlgorithm_sptr alg = + algorithmWithoutOptionalPropertiesSet(setup.inputWSName); + + TS_ASSERT_THROWS(alg->execute(), std::runtime_error); + } + + void + test_that_group_names_with_alphanumeric_characters_or_underscores_are_allowed() { + auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "group1"); + + std::vector<std::string> validNames = {"fwd", "fwd2", "bwd_2"}; + for (auto &&validName : validNames) { + auto alg = setUpAlgorithmWithGroupName(ws, validName); + TSM_ASSERT_THROWS_NOTHING("", alg->execute()); + } + } + + void + test_that_exec_throws_if_group_name_is_not_alphanumeric_or_underscored() { + auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "group1"); + + std::vector<std::string> invalidNames = {"@", "fwd!", "#1", "fwd @", " "}; + for (auto &&invalidName : invalidNames) { + auto alg = setUpAlgorithmWithGroupName(ws, invalidName); + TS_ASSERT_THROWS_ANYTHING(alg->execute()); + } + } + + void + test_that_cannot_add_spectra_to_group_which_exceed_those_in_the_workspace() { + auto ws = createMultiPeriodWorkspaceGroup(1, 5, 10, "group1"); + + std::vector<int> detectors = {6, 7, 8, 9, 10}; + auto alg = setUpAlgorithmWithGroupNameAndDetectors(ws, "group1", detectors); + alg->setRethrows(true); + + TS_ASSERT_THROWS_ANYTHING(alg->execute()); + TS_ASSERT(!alg->isExecuted()); + } + + // -------------------------------------------------------------------------- + // Validation : multi period data + // -------------------------------------------------------------------------- + + void test_that_at_least_one_period_must_be_specified() { + auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group"); + std::vector<int> detectors = {1, 2}; + auto alg = setUpAlgorithmWithGroupNameAndDetectors(ws, "group", detectors); + + std::vector<int> summedPeriods = {}; + std::vector<int> subtractedPeriods = {}; + alg->setProperty("SummedPeriods", summedPeriods); + alg->setProperty("SubtractedPeriods", subtractedPeriods); + + TS_ASSERT_THROWS(alg->execute(), std::runtime_error); + } + + void + test_that_supplying_too_many_periods_to_SummedPeriods_throws_on_execute() { + auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group"); + std::vector<int> detectors = {1, 2, 3}; + auto alg = setUpAlgorithmWithGroupNameAndDetectors(ws, "group", detectors); + + std::vector<int> summedPeriods = {3}; + alg->setProperty("SummedPeriods", summedPeriods); + + TS_ASSERT_THROWS(alg->execute(), std::runtime_error); + } + + void + test_that_supplying_too_many_periods_to_SubtractedPeriods_throws_on_execute() { + auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group"); + std::vector<int> detectors = {1, 2, 3}; + auto alg = setUpAlgorithmWithGroupNameAndDetectors(ws, "group", detectors); + + std::vector<int> subtractedPeriods = {3}; + alg->setProperty("SubtractedPeriods", subtractedPeriods); + + TS_ASSERT_THROWS(alg->execute(), std::runtime_error); + } + + // -------------------------------------------------------------------------- + // Correct Output + // -------------------------------------------------------------------------- + + void test_that_single_period_data_combines_detectors_correctly() { + // Spec 1 y-vals : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + // Spec 2 y-vals : 11, 12, 13, 14, 15, 16 ,17, 18, 19, 20 + // Spec 3 y-vals : 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 + auto ws = createMultiPeriodWorkspaceGroup(1, 3, 10, "group"); + std::vector<int> detectors = {1, 2, 3}; + auto alg = setUpAlgorithmWithGroupNameAndDetectors(ws, "group", detectors); + alg->execute(); + + auto wsOut = getOutputWorkspace(alg); + + TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001); + TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001); + TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001); + + TS_ASSERT_DELTA(wsOut->readY(0)[0], 33.000, 0.001); + TS_ASSERT_DELTA(wsOut->readY(0)[4], 45.000, 0.001); + TS_ASSERT_DELTA(wsOut->readY(0)[9], 60.000, 0.001); + // Quadrature errors : Sqrt(3 * 0.005^2 ) + TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.00866, 0.0001); + TS_ASSERT_DELTA(wsOut->readE(0)[4], 0.00866, 0.0001); + TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.00866, 0.0001); + } + + void test_that_summing_periods_combines_detectors_correctly() { + // Period 1 : + // Spec 1 y-vals : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + // Spec 2 y-vals : 11, 12, 13, 14, 15, 16 ,17, 18, 19, 20 + // Period 2 : + // Spec 1 y-vals : 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + // Spec 2 y-vals : 12, 13, 14, 15, 16, 17 ,18, 19, 20, 21 + auto ws = createMultiPeriodWorkspaceGroup(2, 2, 10, "group"); + std::vector<int> detectors = {1, 2}; + std::vector<int> summedPeriods = {1, 2}; + auto alg = setUpAlgorithmWithGroupNameAndDetectors(ws, "group", detectors); + alg->setProperty("SummedPeriods", summedPeriods); + alg->execute(); + + auto wsOut = getOutputWorkspace(alg); + + TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001); + TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001); + TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001); + + TS_ASSERT_DELTA(wsOut->readY(0)[0], 26.000, 0.001); + TS_ASSERT_DELTA(wsOut->readY(0)[4], 42.000, 0.001); + TS_ASSERT_DELTA(wsOut->readY(0)[9], 62.000, 0.001); + // Quadrature errors : Sqrt(4 * 0.005^2 ) + TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0100, 0.0001); + TS_ASSERT_DELTA(wsOut->readE(0)[4], 0.0100, 0.0001); + TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.0100, 0.0001); + } + + void test_that_subtracting_periods_combines_detectors_correctly() { + // Period 1 : + // Spec 1 y-vals : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + // Spec 2 y-vals : 11, 12, 13, 14, 15, 16 ,17, 18, 19, 20 + // Period 2 : + // Spec 1 y-vals : 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + // Spec 2 y-vals : 12, 13, 14, 15, 16, 17 ,18, 19, 20, 21 + auto ws = createMultiPeriodWorkspaceGroup(2, 2, 10, "group"); + std::vector<int> detectors = {1, 2}; + std::vector<int> summedPeriods = {2}; + std::vector<int> subtractedPeriods = {1}; + auto alg = setUpAlgorithmWithGroupNameAndDetectors(ws, "group", detectors); + alg->setProperty("SummedPeriods", summedPeriods); + alg->setProperty("SubtractedPeriods", subtractedPeriods); + alg->execute(); + + auto wsOut = getOutputWorkspace(alg); + + TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001); + TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001); + TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001); + + TS_ASSERT_DELTA(wsOut->readY(0)[0], 2.000, 0.001); + TS_ASSERT_DELTA(wsOut->readY(0)[4], 2.000, 0.001); + TS_ASSERT_DELTA(wsOut->readY(0)[9], 2.000, 0.001); + // Quadrature errors : Sqrt(4 * 0.005^2 ) + TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0100, 0.0001); + TS_ASSERT_DELTA(wsOut->readE(0)[4], 0.0100, 0.0001); + TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.0100, 0.0001); + } +}; + +#endif /* MANTID_MUON_MUONGROUPINGCOUNTSTEST_H_ */ diff --git a/docs/source/algorithms/MuonGroupingCounts-v1.rst b/docs/source/algorithms/MuonGroupingCounts-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..7e1f8eaffecd5d861b34f8fa1f59c480944be85f --- /dev/null +++ b/docs/source/algorithms/MuonGroupingCounts-v1.rst @@ -0,0 +1,153 @@ +.. algorithm:: + +.. summary:: + +.. relatedalgorithms:: + +.. properties:: + +Description +----------- + +When interacting with the :ref:`Muon_Analysis-ref` interface, operations such as detector grouping, group asymmetry and pair asymmetry are performed on data. This algorithm performs a "grouping counts" operation, in other words it sums the counts associated to a given sequence of detector IDs. + +This algorithm is part of a set of four; with :ref:`algm-MuonPreProcess` being run first; and the output being fed into this one. This allows the replication of the workflow used by the muon analysis interface to produce group data. + +Analysis +######## + +A workspace has one or more *spectra* contained within it; for muon data each spectra has a unique detector ID. Assuming the y-values represent counts; a *detector grouping* operation causes the counts to be summed across the given set of detector IDs which are supplied to the **Grouping** argument (for example `1,2,3,4,5` and `1-5`). + +The **InputWorkspace** must be a *WorkspaceGroup*, where each workspace within the workspace-group represents a single period. Thus, single period data is just a *workspaceGroup* with a single workspace within it. + +The detector-group must be given a name via **GroupName** which can consist of letters, numbers and underscores. + +#. Valid names : "fwd", "fwd2", "fwd_2", "1234" +#. Invalid names : "", "fwd!", "fwd " + +The detector-group name does not affect the data; however the name is used in the muon interface when automatically generating workspace names from detector-group data. + +Multi period data +################# + +Both single and multi period data are supported by the algorithm. + +The **SummedPeriods** and **SubtractedPeriods** inputs are used to control the way that periods are combined. so for example; + +#. SummedPeriods = 1,2 +#. SubtractedPeriods = 3,4 + +would combine periods in the combination $(1+2)-(3+4)$. + +Usage +----- + +**Example - Using MuonPreProcess followed by MuonGroupingCounts on Single Period Data** + +.. testcode:: ConvertToGroup + + # Single period data with four spectra + dataX = [0, 1, 2, 3, 4, 5] * 4 + dataY = [10, 20, 30, 20, 10] * 4 + input_workspace = CreateWorkspace(dataX, dataY, NSpec=4) + for i in range(4): + # set detector IDs to be 1,2,3,4 + # these do not have to be the same as the spectrum numbers + # (the spectrum number are 0,1,2,3 in this case) + input_workspace.getSpectrum(i).setDetectorID(i + 1) + + # We are not actually applying any processing to the data + # but the algorithm will convert our single period data into + # the required form (a WorkspaceGroup) + pre_processed_workspace = MuonPreProcess(InputWorkspace=input_workspace) + + output_workspace = MuonGroupingCounts(InputWorkspace=pre_processed_workspace, + GroupName="fwd", + Grouping=[1, 2, 3, 4]) + + print("X values are : {}".format(output_workspace.readX(0))) + print("Y values are : {}".format(output_workspace.readY(0))) + + +Output: + +.. testoutput:: ConvertToGroup + + X values are : [ 0. 1. 2. 3. 4. 5.] + Y values are : [ 40. 80. 120. 80. 40.] + +**Example - Using Only MuonGroupingCounts on Single Period Data** + +.. testcode:: ConvertToGroup + + # Create a workspaces with four spectra + dataX = [0, 1, 2, 3, 4, 5] * 4 + dataY = [10, 20, 30, 20, 10] * 4 + ws = CreateWorkspace(dataX, dataY, NSpec=4) + for i in range(4): + # set detector IDs to be 1,2,3,4 + # these do not have to be the same as the spectrum numbers + # (the spectrum number are 0,1,2,3 in this case) + ws.getSpectrum(i).setDetectorID(i + 1) + + # Put the workspace inside a WorkspaceGroup + input_workspace = GroupWorkspaces(ws) + + output_workspace = MuonGroupingCounts(InputWorkspace=input_workspace, + GroupName="fwd", + Grouping=[1, 2, 3, 4]) + + print("X values are : {}".format(output_workspace.readX(0))) + print("Y values are : {}".format(output_workspace.readY(0))) + + +Output: + +.. testoutput:: ConvertToGroup + + X values are : [ 0. 1. 2. 3. 4. 5.] + Y values are : [ 40. 80. 120. 80. 40.] + +**Example - Multi Period Data** + +.. testcode:: ExampleTimeOffset + + # Create two workspaces with four spectra + dataX = [0, 1, 2, 3, 4, 5] * 4 + dataY = [10, 20, 30, 20, 10] * 4 + ws1 = CreateWorkspace(dataX, dataY, NSpec=4) + ws2 = CreateWorkspace(dataX, dataY, NSpec=4) + for i in range(4): + # set detector IDs to be 1,2,3,4 + # these do not have to be the same as the spectrum numbers + # (the spectrum number are 0,1,2,3 in this case) + ws1.getSpectrum(i).setDetectorID(i + 1) + ws2.getSpectrum(i).setDetectorID(i + 1) + + # Create multi period data + multi_period_data = GroupWorkspaces(ws1) + multi_period_data.addWorkspace(ws2) + + # This time we won't run MuonPreProcess, as we don't want to apply any pre-processing + # and we already have a WorkspaceGroup + + output_workspace = MuonGroupingCounts(InputWorkspace=multi_period_data, + GroupName="fwd", + Grouping=[1, 2, 3, 4], + SummedPeriods=[1, 2]) + + # We have asked for periods 1+2, with each period summing detectors 1,2,3,4 + print("X values are : {}".format(output_workspace.readX(0))) + print("Y values are : {}".format(output_workspace.readY(0))) + + +Output: + +.. testoutput:: ExampleTimeOffset + + X values are : [ 0. 1. 2. 3. 4. 5.] + Y values are : [ 80. 160. 240. 160. 80.] + +.. categories:: + +.. sourcelink:: \ No newline at end of file