From 2a0ddd89ebd777f37ae66ad09b4890e54836f1dd Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Tue, 15 Oct 2024 16:30:52 +1100 Subject: [PATCH 1/4] Add donor to ExtractMask --- .../inc/MantidAlgorithms/ExtractMask.h | 1 + Framework/Algorithms/src/ExtractMask.cpp | 60 +++++++++++-------- Framework/Algorithms/test/ExtractMaskTest.h | 7 ++- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h b/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h index 34dc210e5f39..35275e239a38 100644 --- a/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h +++ b/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h @@ -44,6 +44,7 @@ class MANTID_ALGORITHMS_DLL ExtractMask : public Mantid::API::Algorithm { const std::vector seeAlso() const override { return {"ExtractMaskToTable"}; } /// Algorithm's category for identification const std::string category() const override { return "Transforms\\Masking"; } + std::map validateInputs() override; private: /// Initialisation code diff --git a/Framework/Algorithms/src/ExtractMask.cpp b/Framework/Algorithms/src/ExtractMask.cpp index f74ae3705128..ec878b2d47f4 100644 --- a/Framework/Algorithms/src/ExtractMask.cpp +++ b/Framework/Algorithms/src/ExtractMask.cpp @@ -26,8 +26,12 @@ using namespace Kernel; * Declare the algorithm properties */ void ExtractMask::init() { - declareProperty(std::make_unique>("InputWorkspace", "", Direction::Input), + declareProperty(std::make_unique>("InputWorkspace", "", Direction::Input, + PropertyMode::Optional), "A workspace whose masking is to be extracted"); + declareProperty(std::make_unique>("InstrumentDonor", "", Direction::Input, + PropertyMode::Optional), + "Optional: A workspace whose instrument will be used for the output instead of the InputWorkspace"); declareProperty(std::make_unique>("OutputWorkspace", "", Direction::Output), "A workspace containing the masked spectra as zeroes and ones."); @@ -37,11 +41,21 @@ void ExtractMask::init() { "detector ID's"); } +std::map ExtractMask::validateInputs() { + std::map errors; + if (isDefault("InputWorkspace") && isDefault("InstrumentDonor")) { + errors["InputWorkspace"] = "Either InputWorkspace or InstrumentDonor is required"; + errors["InstrumentDonor"] = "Either InputWorkspace or InstrumentDonor is required"; + } + return errors; +} + /** * Execute the algorithm */ void ExtractMask::exec() { MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace"); + MatrixWorkspace_const_sptr donorWS = getProperty("InstrumentDonor"); // convert input to a mask workspace auto inputMaskWS = std::dynamic_pointer_cast(inputWS); @@ -52,36 +66,34 @@ void ExtractMask::exec() { // List masked of detector IDs std::vector detectorList; - const auto &detInfo = inputWS->detectorInfo(); - const auto &detIds = detInfo.detectorIDs(); - for (size_t i = 0; i < detInfo.size(); ++i) { - if ((inputWSIsSpecial && inputMaskWS->isMasked(detIds[i])) || detInfo.isMasked(i)) { - detectorList.emplace_back(detIds[i]); + if (inputWS) { + const auto &detInfo = inputWS->detectorInfo(); + const auto &detIds = detInfo.detectorIDs(); + for (size_t i = 0; i < detInfo.size(); ++i) { + if ((inputWSIsSpecial && inputMaskWS->isMasked(detIds[i])) || detInfo.isMasked(i)) { + detectorList.emplace_back(detIds[i]); + } } } - // Create a new workspace for the results, copy from the input to ensure // that we copy over the instrument and current masking - auto maskWS = std::make_shared(inputWS); - maskWS->setTitle(inputWS->getTitle()); - - const auto &spectrumInfo = inputWS->spectrumInfo(); - const auto nHist = static_cast(inputWS->getNumberHistograms()); - Progress prog(this, 0.0, 1.0, nHist); + std::shared_ptr maskWS; + if (donorWS) { + maskWS = std::make_shared(donorWS->getInstrument()); + maskWS->setTitle(donorWS->getTitle()); + } else { + maskWS = std::make_shared(inputWS); + maskWS->setTitle(inputWS->getTitle()); + } - PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *maskWS)) - for (int64_t i = 0; i < nHist; ++i) { - PARALLEL_START_INTERRUPT_REGION - bool inputIsMasked(false); - if (spectrumInfo.hasDetectors(i)) { - // special workspaces can mysteriously have the mask bit set - inputIsMasked = (inputWSIsSpecial && inputMaskWS->isMaskedIndex(i)) || spectrumInfo.isMasked(i); + // set mask from from detectorList + for (auto detectorID : detectorList) { + try { + maskWS->setMasked(detectorID); + } catch (std::invalid_argument const &) { + g_log.warning() << "Detector ID = " << detectorID << " is masked but does not exist in any output spectra\n"; } - maskWS->setMaskedIndex(i, inputIsMasked); - prog.report(); - PARALLEL_END_INTERRUPT_REGION } - PARALLEL_CHECK_INTERRUPT_REGION g_log.information() << maskWS->getNumberMasked() << " spectra are masked\n"; g_log.information() << detectorList.size() << " detectors are masked\n"; diff --git a/Framework/Algorithms/test/ExtractMaskTest.h b/Framework/Algorithms/test/ExtractMaskTest.h index 1e8aa7c7283d..5364a21389fb 100644 --- a/Framework/Algorithms/test/ExtractMaskTest.h +++ b/Framework/Algorithms/test/ExtractMaskTest.h @@ -25,10 +25,11 @@ class ExtractMaskTest : public CxxTest::TestSuite { ExtractMask maskExtractor; maskExtractor.initialize(); std::vector properties = maskExtractor.getProperties(); - TS_ASSERT_EQUALS(properties.size(), 3); - if (properties.size() == 3) { + TS_ASSERT_EQUALS(properties.size(), 4); + if (properties.size() == 4) { TS_ASSERT_EQUALS(properties[0]->name(), "InputWorkspace"); - TS_ASSERT_EQUALS(properties[1]->name(), "OutputWorkspace"); + TS_ASSERT_EQUALS(properties[1]->name(), "InstrumentDonor"); + TS_ASSERT_EQUALS(properties[2]->name(), "OutputWorkspace"); } } From 52d02199310f56b60a9880e9b5483a3074246015 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Wed, 16 Oct 2024 15:53:22 +1100 Subject: [PATCH 2/4] Add test and docs --- Framework/Algorithms/test/ExtractMaskTest.h | 68 ++++++++++++++++++++- docs/source/algorithms/ExtractMask-v1.rst | 7 +++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/Framework/Algorithms/test/ExtractMaskTest.h b/Framework/Algorithms/test/ExtractMaskTest.h index 5364a21389fb..f533a323614f 100644 --- a/Framework/Algorithms/test/ExtractMaskTest.h +++ b/Framework/Algorithms/test/ExtractMaskTest.h @@ -6,6 +6,7 @@ // SPDX - License - Identifier: GPL - 3.0 + #pragma once +#include "MantidAPI/AlgorithmFactory.h" #include "MantidAPI/SpectrumInfo.h" #include "MantidAlgorithms/ExtractMask.h" #include "MantidDataObjects/MaskWorkspace.h" @@ -118,12 +119,77 @@ class ExtractMaskTest : public CxxTest::TestSuite { AnalysisDataService::Instance().remove(inputName); } + void test_donor_instrument() { + // Create a simple test workspace with grouped detectors + const std::string inputName("inputWS"); + auto createWS = AlgorithmFactory::Instance().create("CreateSampleWorkspace", -1); + createWS->initialize(); + createWS->setProperty("NumBanks", 4); + createWS->setProperty("BankPixelWidth", 2); + createWS->setPropertyValue("OutputWorkspace", inputName); + createWS->execute(); + + auto inputWS = AnalysisDataService::Instance().retrieveWS(inputName); + Workspace_sptr donorWS = inputWS->clone(); + const std::string donorName("donorWS"); + AnalysisDataService::Instance().add(donorName, donorWS); + + const std::string groupName("groupWS"); + auto createGroupWS = AlgorithmFactory::Instance().create("CreateGroupingWorkspace", -1); + createGroupWS->initialize(); + createGroupWS->setPropertyValue("InputWorkspace", inputName); + createGroupWS->setPropertyValue("GroupDetectorsBy", "bank"); + createGroupWS->setPropertyValue("OutputWorkspace", groupName); + createGroupWS->execute(); + + auto groupWS = AlgorithmFactory::Instance().create("GroupDetectors", -1); + groupWS->initialize(); + groupWS->setPropertyValue("InputWorkspace", inputName); + groupWS->setPropertyValue("CopyGroupingFromWorkspace", groupName); + groupWS->setPropertyValue("OutputWorkspace", inputName); + groupWS->execute(); + + // mask spectra 0 and 2 which will be detectors 1-4 and 9-12 + auto mask = AlgorithmFactory::Instance().create("MaskDetectors", -1); + mask->initialize(); + mask->setPropertyValue("Workspace", inputName); + mask->setPropertyValue("WorkspaceIndexList", "0,2"); + mask->execute(); + + inputWS = AnalysisDataService::Instance().retrieveWS(inputName); + TS_ASSERT_EQUALS(inputWS->getNumberHistograms(), 4); + + MaskWorkspace_sptr outputWS; + TS_ASSERT_THROWS_NOTHING(outputWS = runExtractMask(inputName, donorName)); + TS_ASSERT(outputWS); + if (outputWS) { + TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 16); + TS_ASSERT_EQUALS(outputWS->getNumberMasked(), 8); + for (size_t i = 0; i < 4; ++i) + TS_ASSERT_EQUALS(outputWS->isMaskedIndex(i), true); + for (size_t i = 4; i < 8; ++i) + TS_ASSERT_EQUALS(outputWS->isMaskedIndex(i), false); + for (size_t i = 8; i < 12; ++i) + TS_ASSERT_EQUALS(outputWS->isMaskedIndex(i), true); + for (size_t i = 12; i < 16; ++i) + TS_ASSERT_EQUALS(outputWS->isMaskedIndex(i), false); + } + + AnalysisDataService::Instance().remove(inputName); + AnalysisDataService::Instance().remove(donorName); + AnalysisDataService::Instance().remove(groupName); + AnalysisDataService::Instance().remove(outputWS->getName()); + } + private: // The input workspace should be in the analysis data service - MaskWorkspace_sptr runExtractMask(const std::string &inputName) { + MaskWorkspace_sptr runExtractMask(const std::string &inputName, const std::string &donorName = "") { ExtractMask maskExtractor; maskExtractor.initialize(); maskExtractor.setPropertyValue("InputWorkspace", inputName); + if (!donorName.empty()) { + maskExtractor.setPropertyValue("InstrumentDonor", donorName); + } const std::string outputName("masking"); maskExtractor.setPropertyValue("OutputWorkspace", outputName); maskExtractor.setRethrows(true); diff --git a/docs/source/algorithms/ExtractMask-v1.rst b/docs/source/algorithms/ExtractMask-v1.rst index fe2ec8f9976f..0cb7fe6de7c8 100644 --- a/docs/source/algorithms/ExtractMask-v1.rst +++ b/docs/source/algorithms/ExtractMask-v1.rst @@ -21,6 +21,13 @@ link is preserved so that the instrument view functions correctly. A list of masked detector IDs is also output. Note this contains the detector IDs which are masked rather than the index or spectrum number. +Using Instrument donor +---------------------- + +The ``InstrumentDonor`` parameter can be used to specify a workspace from which the ``MaskWorkspace`` creation instrument is based on. The masking will still be copied from the ``InputWorkspace`` but will be applied by matching the detector IDs from the ``InstrumentDonor`` workspace. + +You can specify the ``InstrumentDonor`` without the ``InputWorkspace``. In this case, the output ``MaskWorkspace`` will have all spectra unmasked. + Usage ----- From 4405f954b6e474bf756608cf6a726bfbe95c6693 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Wed, 16 Oct 2024 16:31:02 +1100 Subject: [PATCH 3/4] Add release note --- .../release/v6.12.0/Framework/Algorithms/New_features/38198.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst diff --git a/docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst b/docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst new file mode 100644 index 000000000000..c0a69c03baa1 --- /dev/null +++ b/docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst @@ -0,0 +1 @@ +- A new property ``InstrumentDonor`` was added to :ref:`ExtractMask ` allowing you to specify which instrument is used when creating the ``MaskWorkspace``. From 76f198455b05fdedc09cc90acbc320c4cd8491e3 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Mon, 21 Oct 2024 09:55:26 +1100 Subject: [PATCH 4/4] Move to UngroupDetectors option --- .../inc/MantidAlgorithms/ExtractMask.h | 1 - Framework/Algorithms/src/ExtractMask.cpp | 46 ++++++------------- Framework/Algorithms/test/ExtractMaskTest.h | 20 +++----- docs/source/algorithms/ExtractMask-v1.rst | 8 ++-- .../Algorithms/New_features/38198.rst | 2 +- 5 files changed, 25 insertions(+), 52 deletions(-) diff --git a/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h b/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h index 35275e239a38..34dc210e5f39 100644 --- a/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h +++ b/Framework/Algorithms/inc/MantidAlgorithms/ExtractMask.h @@ -44,7 +44,6 @@ class MANTID_ALGORITHMS_DLL ExtractMask : public Mantid::API::Algorithm { const std::vector seeAlso() const override { return {"ExtractMaskToTable"}; } /// Algorithm's category for identification const std::string category() const override { return "Transforms\\Masking"; } - std::map validateInputs() override; private: /// Initialisation code diff --git a/Framework/Algorithms/src/ExtractMask.cpp b/Framework/Algorithms/src/ExtractMask.cpp index ec878b2d47f4..bf8084cdfbfd 100644 --- a/Framework/Algorithms/src/ExtractMask.cpp +++ b/Framework/Algorithms/src/ExtractMask.cpp @@ -26,12 +26,9 @@ using namespace Kernel; * Declare the algorithm properties */ void ExtractMask::init() { - declareProperty(std::make_unique>("InputWorkspace", "", Direction::Input, - PropertyMode::Optional), + declareProperty(std::make_unique>("InputWorkspace", "", Direction::Input), "A workspace whose masking is to be extracted"); - declareProperty(std::make_unique>("InstrumentDonor", "", Direction::Input, - PropertyMode::Optional), - "Optional: A workspace whose instrument will be used for the output instead of the InputWorkspace"); + declareProperty("UngroupDetectors", false, "If true, the spectra will be ungrouped and masked individually."); declareProperty(std::make_unique>("OutputWorkspace", "", Direction::Output), "A workspace containing the masked spectra as zeroes and ones."); @@ -41,21 +38,11 @@ void ExtractMask::init() { "detector ID's"); } -std::map ExtractMask::validateInputs() { - std::map errors; - if (isDefault("InputWorkspace") && isDefault("InstrumentDonor")) { - errors["InputWorkspace"] = "Either InputWorkspace or InstrumentDonor is required"; - errors["InstrumentDonor"] = "Either InputWorkspace or InstrumentDonor is required"; - } - return errors; -} - /** * Execute the algorithm */ void ExtractMask::exec() { MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace"); - MatrixWorkspace_const_sptr donorWS = getProperty("InstrumentDonor"); // convert input to a mask workspace auto inputMaskWS = std::dynamic_pointer_cast(inputWS); @@ -66,34 +53,31 @@ void ExtractMask::exec() { // List masked of detector IDs std::vector detectorList; - if (inputWS) { - const auto &detInfo = inputWS->detectorInfo(); - const auto &detIds = detInfo.detectorIDs(); - for (size_t i = 0; i < detInfo.size(); ++i) { - if ((inputWSIsSpecial && inputMaskWS->isMasked(detIds[i])) || detInfo.isMasked(i)) { - detectorList.emplace_back(detIds[i]); - } + const auto &detInfo = inputWS->detectorInfo(); + const auto &detIds = detInfo.detectorIDs(); + for (size_t i = 0; i < detInfo.size(); ++i) { + if ((inputWSIsSpecial && inputMaskWS->isMasked(detIds[i])) || detInfo.isMasked(i)) { + detectorList.emplace_back(detIds[i]); } } + // Create a new workspace for the results, copy from the input to ensure // that we copy over the instrument and current masking std::shared_ptr maskWS; - if (donorWS) { - maskWS = std::make_shared(donorWS->getInstrument()); - maskWS->setTitle(donorWS->getTitle()); - } else { + if (getProperty("UngroupDetectors")) + maskWS = std::make_shared(inputWS->getInstrument()); + else maskWS = std::make_shared(inputWS); - maskWS->setTitle(inputWS->getTitle()); - } + + maskWS->setTitle(inputWS->getTitle()); // set mask from from detectorList - for (auto detectorID : detectorList) { + for (auto detectorID : detectorList) try { maskWS->setMasked(detectorID); } catch (std::invalid_argument const &) { - g_log.warning() << "Detector ID = " << detectorID << " is masked but does not exist in any output spectra\n"; + g_log.warning() << "Detector ID = " << detectorID << " is masked but does not exist in any output spectra\n "; } - } g_log.information() << maskWS->getNumberMasked() << " spectra are masked\n"; g_log.information() << detectorList.size() << " detectors are masked\n"; diff --git a/Framework/Algorithms/test/ExtractMaskTest.h b/Framework/Algorithms/test/ExtractMaskTest.h index f533a323614f..2dffe4e72b76 100644 --- a/Framework/Algorithms/test/ExtractMaskTest.h +++ b/Framework/Algorithms/test/ExtractMaskTest.h @@ -29,7 +29,7 @@ class ExtractMaskTest : public CxxTest::TestSuite { TS_ASSERT_EQUALS(properties.size(), 4); if (properties.size() == 4) { TS_ASSERT_EQUALS(properties[0]->name(), "InputWorkspace"); - TS_ASSERT_EQUALS(properties[1]->name(), "InstrumentDonor"); + TS_ASSERT_EQUALS(properties[1]->name(), "UngroupDetectors"); TS_ASSERT_EQUALS(properties[2]->name(), "OutputWorkspace"); } } @@ -119,7 +119,7 @@ class ExtractMaskTest : public CxxTest::TestSuite { AnalysisDataService::Instance().remove(inputName); } - void test_donor_instrument() { + void test_ungroup_detectors() { // Create a simple test workspace with grouped detectors const std::string inputName("inputWS"); auto createWS = AlgorithmFactory::Instance().create("CreateSampleWorkspace", -1); @@ -129,11 +129,6 @@ class ExtractMaskTest : public CxxTest::TestSuite { createWS->setPropertyValue("OutputWorkspace", inputName); createWS->execute(); - auto inputWS = AnalysisDataService::Instance().retrieveWS(inputName); - Workspace_sptr donorWS = inputWS->clone(); - const std::string donorName("donorWS"); - AnalysisDataService::Instance().add(donorName, donorWS); - const std::string groupName("groupWS"); auto createGroupWS = AlgorithmFactory::Instance().create("CreateGroupingWorkspace", -1); createGroupWS->initialize(); @@ -156,11 +151,11 @@ class ExtractMaskTest : public CxxTest::TestSuite { mask->setPropertyValue("WorkspaceIndexList", "0,2"); mask->execute(); - inputWS = AnalysisDataService::Instance().retrieveWS(inputName); + auto inputWS = AnalysisDataService::Instance().retrieveWS(inputName); TS_ASSERT_EQUALS(inputWS->getNumberHistograms(), 4); MaskWorkspace_sptr outputWS; - TS_ASSERT_THROWS_NOTHING(outputWS = runExtractMask(inputName, donorName)); + TS_ASSERT_THROWS_NOTHING(outputWS = runExtractMask(inputName, true)); TS_ASSERT(outputWS); if (outputWS) { TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 16); @@ -176,20 +171,17 @@ class ExtractMaskTest : public CxxTest::TestSuite { } AnalysisDataService::Instance().remove(inputName); - AnalysisDataService::Instance().remove(donorName); AnalysisDataService::Instance().remove(groupName); AnalysisDataService::Instance().remove(outputWS->getName()); } private: // The input workspace should be in the analysis data service - MaskWorkspace_sptr runExtractMask(const std::string &inputName, const std::string &donorName = "") { + MaskWorkspace_sptr runExtractMask(const std::string &inputName, const bool ungroupDetectors = false) { ExtractMask maskExtractor; maskExtractor.initialize(); maskExtractor.setPropertyValue("InputWorkspace", inputName); - if (!donorName.empty()) { - maskExtractor.setPropertyValue("InstrumentDonor", donorName); - } + maskExtractor.setProperty("UngroupDetectors", ungroupDetectors); const std::string outputName("masking"); maskExtractor.setPropertyValue("OutputWorkspace", outputName); maskExtractor.setRethrows(true); diff --git a/docs/source/algorithms/ExtractMask-v1.rst b/docs/source/algorithms/ExtractMask-v1.rst index 0cb7fe6de7c8..5f39f6653099 100644 --- a/docs/source/algorithms/ExtractMask-v1.rst +++ b/docs/source/algorithms/ExtractMask-v1.rst @@ -21,12 +21,10 @@ link is preserved so that the instrument view functions correctly. A list of masked detector IDs is also output. Note this contains the detector IDs which are masked rather than the index or spectrum number. -Using Instrument donor ----------------------- +Ungroup Detectors +----------------- -The ``InstrumentDonor`` parameter can be used to specify a workspace from which the ``MaskWorkspace`` creation instrument is based on. The masking will still be copied from the ``InputWorkspace`` but will be applied by matching the detector IDs from the ``InstrumentDonor`` workspace. - -You can specify the ``InstrumentDonor`` without the ``InputWorkspace``. In this case, the output ``MaskWorkspace`` will have all spectra unmasked. +If ``UngroupDetectors`` is set to True, the detector IDs are ungrouped. This means that if the input workspace has a grouped detector, the output workspace will have a single spectra for each detector. Usage ----- diff --git a/docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst b/docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst index c0a69c03baa1..89939a4fcff1 100644 --- a/docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst +++ b/docs/source/release/v6.12.0/Framework/Algorithms/New_features/38198.rst @@ -1 +1 @@ -- A new property ``InstrumentDonor`` was added to :ref:`ExtractMask ` allowing you to specify which instrument is used when creating the ``MaskWorkspace``. +- A new property ``UngroupDetectors`` was added to :ref:`ExtractMask ` allowing the output ``MaskWorkspace`` to expand the spectra to individal detectors. \ No newline at end of file