diff --git a/Framework/API/CMakeLists.txt b/Framework/API/CMakeLists.txt index 05529388b2621945dceac437546e5087658091a4..5cf25c750d09a7725241e29a9765dc93c935e112 100644 --- a/Framework/API/CMakeLists.txt +++ b/Framework/API/CMakeLists.txt @@ -95,8 +95,8 @@ set ( SRC_FILES src/LogFilterGenerator.cpp src/LogManager.cpp src/LogarithmScale.cpp + src/MDFrameValidator.cpp src/MDGeometry.cpp - src/MDFrameValidator.cpp src/MatrixWorkspace.cpp src/MatrixWorkspaceMDIterator.cpp src/ModeratorModel.cpp @@ -111,7 +111,7 @@ set ( SRC_FILES src/NullCoordTransform.cpp src/NumericAxis.cpp src/NumericAxisValidator.cpp - src/OrientedLatticeValidator.cpp + src/OrientedLatticeValidator.cpp src/ParallelAlgorithm.cpp src/ParamFunction.cpp src/ParameterReference.cpp @@ -144,6 +144,7 @@ set ( SRC_FILES src/Workspace.cpp src/WorkspaceFactory.cpp src/WorkspaceGroup.cpp + src/WorkspaceHasDxValidator.cpp src/WorkspaceHistory.cpp src/WorkspaceNearestNeighbourInfo.cpp src/WorkspaceNearestNeighbours.cpp @@ -293,8 +294,8 @@ set ( INC_FILES inc/MantidAPI/LogFilterGenerator.h inc/MantidAPI/LogManager.h inc/MantidAPI/LogarithmScale.h + inc/MantidAPI/MDFrameValidator.h inc/MantidAPI/MDGeometry.h - inc/MantidAPI/MDFrameValidator.h inc/MantidAPI/MatrixWorkspace.h inc/MantidAPI/MatrixWorkspaceMDIterator.h inc/MantidAPI/MatrixWorkspaceValidator.h @@ -339,6 +340,8 @@ set ( INC_FILES inc/MantidAPI/SpectraAxisValidator.h inc/MantidAPI/SpectrumDetectorMapping.h inc/MantidAPI/SpectrumInfo.h + inc/MantidAPI/SpectrumInfoItem.h + inc/MantidAPI/SpectrumInfoIterator.h inc/MantidAPI/TableRow.h inc/MantidAPI/TextAxis.h inc/MantidAPI/TransformScaleFactory.h @@ -348,6 +351,7 @@ set ( INC_FILES inc/MantidAPI/WorkspaceFactory.h inc/MantidAPI/WorkspaceGroup.h inc/MantidAPI/WorkspaceGroup_fwd.h + inc/MantidAPI/WorkspaceHasDxValidator.h inc/MantidAPI/WorkspaceHistory.h inc/MantidAPI/WorkspaceNearestNeighbourInfo.h inc/MantidAPI/WorkspaceNearestNeighbours.h @@ -356,8 +360,6 @@ set ( INC_FILES inc/MantidAPI/WorkspaceProperty.tcc inc/MantidAPI/WorkspaceUnitValidator.h inc/MantidAPI/Workspace_fwd.h - inc/MantidAPI/SpectrumInfoIterator.h - inc/MantidAPI/SpectrumInfoItem.h ) set ( TEST_FILES @@ -425,8 +427,8 @@ set ( TEST_FILES LiveListenerTest.h LogFilterGeneratorTest.h LogManagerTest.h - MDGeometryTest.h MDFrameValidatorTest.h + MDGeometryTest.h MatrixWorkspaceMDIteratorTest.h ModeratorModelTest.h MuParserUtilsTest.h @@ -465,6 +467,7 @@ set ( TEST_FILES VectorParameterTest.h WorkspaceFactoryTest.h WorkspaceGroupTest.h + WorkspaceHasDxValidatorTest.h WorkspaceHistoryIOTest.h WorkspaceHistoryTest.h WorkspaceNearestNeighbourInfoTest.h diff --git a/Framework/API/inc/MantidAPI/WorkspaceHasDxValidator.h b/Framework/API/inc/MantidAPI/WorkspaceHasDxValidator.h new file mode 100644 index 0000000000000000000000000000000000000000..e1d9570ac26bd9bcc6ed47797a43f31718589b1c --- /dev/null +++ b/Framework/API/inc/MantidAPI/WorkspaceHasDxValidator.h @@ -0,0 +1,32 @@ +// 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_API_WORKSPACEHASDXVALIDATOR_H_ +#define MANTID_API_WORKSPACEHASDXVALIDATOR_H_ + +#include "MantidAPI/DllConfig.h" + +#include "MantidAPI/MatrixWorkspaceValidator.h" + +namespace Mantid { +namespace API { + +/** WorkspaceHasDxValidator : A validator which checks that all histograms in a + * workspace have Dx values. + */ +class MANTID_API_DLL WorkspaceHasDxValidator final + : public MatrixWorkspaceValidator { +public: + Kernel::IValidator_sptr clone() const override; + +private: + std::string checkValidity(MatrixWorkspace_sptr const &ws) const override; +}; + +} // namespace API +} // namespace Mantid + +#endif /* MANTID_API_WORKSPACEHASDXVALIDATOR_H_ */ diff --git a/Framework/API/src/WorkspaceHasDxValidator.cpp b/Framework/API/src/WorkspaceHasDxValidator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e2a061d80f875584556cc932edab206b7a379d58 --- /dev/null +++ b/Framework/API/src/WorkspaceHasDxValidator.cpp @@ -0,0 +1,24 @@ +#include "MantidAPI/WorkspaceHasDxValidator.h" + +namespace Mantid { +namespace API { + +/// Return a deep clone of this validator. +Kernel::IValidator_sptr WorkspaceHasDxValidator::clone() const { + return boost::make_shared<WorkspaceHasDxValidator>(*this); +} + +/// Return an error string if not all histograms in ws have Dx, otherwise an +/// empty string. +std::string +WorkspaceHasDxValidator::checkValidity(MatrixWorkspace_sptr const &ws) const { + for (size_t i = 0; i < ws->getNumberHistograms(); ++i) { + if (!ws->hasDx(i)) { + return "The workspace must have Dx values set"; + } + } + return ""; +} + +} // namespace API +} // namespace Mantid diff --git a/Framework/API/test/WorkspaceHasDxValidatorTest.h b/Framework/API/test/WorkspaceHasDxValidatorTest.h new file mode 100644 index 0000000000000000000000000000000000000000..7713fa5e0f213165f770af9feb627eed6c5fea3d --- /dev/null +++ b/Framework/API/test/WorkspaceHasDxValidatorTest.h @@ -0,0 +1,45 @@ +// 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_API_WORKSPACEHASDXVALIDATORTEST_H_ +#define MANTID_API_WORKSPACEHASDXVALIDATORTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAPI/WorkspaceHasDxValidator.h" + +#include "MantidTestHelpers/FakeObjects.h" + +using namespace Mantid; + +class WorkspaceHasDxValidatorTest : 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 WorkspaceHasDxValidatorTest *createSuite() { + return new WorkspaceHasDxValidatorTest(); + } + static void destroySuite(WorkspaceHasDxValidatorTest *suite) { delete suite; } + + void test_returns_empty_string_for_valid_workspaces() { + auto ws = boost::make_shared<WorkspaceTester>(); + ws->initialize(1, 1, 1); + auto dx = Kernel::make_cow<HistogramData::HistogramDx>(1, 0.); + ws->setSharedDx(0, std::move(dx)); + WorkspaceHasDxValidator validator; + TS_ASSERT_EQUALS(validator.isValid(ws), "") + } + + void test_returns_message_for_invalid_workspaces() { + auto ws = boost::make_shared<WorkspaceTester>(); + ws->initialize(1, 1, 1); + WorkspaceHasDxValidator validator; + TS_ASSERT_EQUALS(validator.isValid(ws), + "The workspace must have Dx values set") + } +}; + +#endif /* MANTID_API_WORKSPACEHASDXVALIDATORTEST_H_ */ diff --git a/Framework/Algorithms/CMakeLists.txt b/Framework/Algorithms/CMakeLists.txt index 00b6f25d0e4e30b49aecf9a0fec1b12a90ce3aec..6514e828c709ee4ed3f41a3a853f1362a53525d9 100644 --- a/Framework/Algorithms/CMakeLists.txt +++ b/Framework/Algorithms/CMakeLists.txt @@ -160,6 +160,7 @@ set ( SRC_FILES src/GetQsInQENSData.cpp src/GetTimeSeriesLogInformation.cpp src/GravitySANSHelper.cpp + src/GroupToXResolution.cpp src/GroupWorkspaces.cpp src/HRPDSlabCanAbsorption.cpp src/He3TubeEfficiency.cpp @@ -494,6 +495,7 @@ set ( INC_FILES inc/MantidAlgorithms/GetQsInQENSData.h inc/MantidAlgorithms/GetTimeSeriesLogInformation.h inc/MantidAlgorithms/GravitySANSHelper.h + inc/MantidAlgorithms/GroupToXResolution.h inc/MantidAlgorithms/GroupWorkspaces.h inc/MantidAlgorithms/HRPDSlabCanAbsorption.h inc/MantidAlgorithms/He3TubeEfficiency.h @@ -701,7 +703,7 @@ set ( TEST_FILES CalculateCarpenterSampleCorrectionTest.h CalculateCountRateTest.h CalculateDIFCTest.h - CalculateDynamicRangeTest.h + CalculateDynamicRangeTest.h CalculateEfficiencyTest.h CalculateFlatBackgroundTest.h CalculateIqtTest.h @@ -835,6 +837,7 @@ set ( TEST_FILES GetEiV1Test.h GetQsInQENSDataTest.h GetTimeSeriesLogInformationTest.h + GroupToXResolutionTest.h GroupWorkspacesTest.h HRPDSlabCanAbsorptionTest.h He3TubeEfficiencyTest.h diff --git a/Framework/Algorithms/inc/MantidAlgorithms/GroupToXResolution.h b/Framework/Algorithms/inc/MantidAlgorithms/GroupToXResolution.h new file mode 100644 index 0000000000000000000000000000000000000000..49a47e66f14134ef9e8c9ce659484945c4503e93 --- /dev/null +++ b/Framework/Algorithms/inc/MantidAlgorithms/GroupToXResolution.h @@ -0,0 +1,35 @@ +// 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_ALGORITHMS_GROUPTOXRESOLUTION_H_ +#define MANTID_ALGORITHMS_GROUPTOXRESOLUTION_H_ + +#include "MantidAPI/Algorithm.h" +#include "MantidAlgorithms/DllConfig.h" + +namespace Mantid { +namespace Algorithms { + +/** GroupToXResolution : Groups points within intervals defined by Dx into a + * single point. + */ +class MANTID_ALGORITHMS_DLL GroupToXResolution : public API::Algorithm { +public: + const std::string name() const override; + int version() const override; + const std::string category() const override; + const std::string summary() const override; + +private: + void init() override; + void exec() override; + std::map<std::string, std::string> validateInputs() override; +}; + +} // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_GROUPTOXRESOLUTION_H_ */ diff --git a/Framework/Algorithms/src/GroupToXResolution.cpp b/Framework/Algorithms/src/GroupToXResolution.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f763fa977d00b087664f5a8f05a43d591dad7fc --- /dev/null +++ b/Framework/Algorithms/src/GroupToXResolution.cpp @@ -0,0 +1,160 @@ +// 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 "MantidAlgorithms/GroupToXResolution.h" + +#include "MantidAPI/HistogramValidator.h" +#include "MantidAPI/WorkspaceHasDxValidator.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidHistogramData/HistogramBuilder.h" +#include "MantidKernel/BoundedValidator.h" +#include "MantidKernel/CompositeValidator.h" + +#include <boost/math/special_functions/pow.hpp> + +namespace { +namespace Prop { +std::string const FRACTION{"FractionOfDx"}; +std::string const INPUT_WS{"InputWorkspace"}; +std::string const OUTPUT_WS{"OutputWorkspace"}; +} // namespace Prop +constexpr double FWHM_GAUSSIAN_EQUIVALENT{0.68}; +} // namespace + +namespace Mantid { +namespace Algorithms { +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(GroupToXResolution) + +/// Algorithms name for identification. @see Algorithm::name +const std::string GroupToXResolution::name() const { + return "GroupToXResolution"; +} + +/// Algorithm's version for identification. @see Algorithm::version +int GroupToXResolution::version() const { return 1; } + +/// Algorithm's category for identification. @see Algorithm::category +const std::string GroupToXResolution::category() const { + return "Transforms\\Rebin"; +} + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string GroupToXResolution::summary() const { + return "Groups points within intervals given by the Dx into single points"; +} + +/** Initialize the algorithm's properties. + */ +void GroupToXResolution::init() { + auto inputValidator = boost::make_shared<Kernel::CompositeValidator>(); + inputValidator->add(boost::make_shared<API::WorkspaceHasDxValidator>()); + constexpr bool acceptHistograms{false}; + inputValidator->add( + boost::make_shared<API::HistogramValidator>(acceptHistograms)); + declareProperty( + Kernel::make_unique<API::WorkspaceProperty<>>( + Prop::INPUT_WS, "", Kernel::Direction::Input, inputValidator), + "An input workspace with Dx values."); + declareProperty(Kernel::make_unique<API::WorkspaceProperty<>>( + Prop::OUTPUT_WS, "", Kernel::Direction::Output), + "The grouped workspace."); + auto positive = boost::make_shared<Kernel::BoundedValidator<double>>(); + positive->setLower(0.); + positive->setLowerExclusive(true); + declareProperty(Prop::FRACTION, 0.2, positive, + "A fraction of Dx to group the points to."); +} + +std::map<std::string, std::string> GroupToXResolution::validateInputs() { + std::map<std::string, std::string> issues; + API::MatrixWorkspace_const_sptr inWS = getProperty(Prop::INPUT_WS); + if (inWS->getNumberHistograms() != 1) { + issues[Prop::INPUT_WS] = + "The workspace should contain only a single histogram."; + } + return issues; +} + +/** Execute the algorithm. + */ +void GroupToXResolution::exec() { + using boost::math::pow; + API::MatrixWorkspace_const_sptr inWS = getProperty(Prop::INPUT_WS); + double const groupingFraction = getProperty(Prop::FRACTION); + HistogramData::Histogram h(HistogramData::Histogram::XMode::Points, + HistogramData::Histogram::YMode::Counts); + auto const &inXs = inWS->x(0); + auto const &inYs = inWS->y(0); + auto const &inEs = inWS->e(0); + auto const &inDxs = inWS->dx(0); + std::vector<double> outXs; + outXs.reserve(inXs.size()); + std::vector<double> outYs; + outYs.reserve(inXs.size()); + std::vector<double> outEs; + outEs.reserve(inXs.size()); + std::vector<double> outDxs; + outDxs.reserve(inXs.size()); + size_t pointIndex{0}; + double begin = inXs.front(); + while (true) { + auto const Dx = inDxs[pointIndex]; + if (Dx <= 0.) { + throw std::out_of_range("Nonpositive DX value in the workspace."); + } + auto const width = groupingFraction * Dx; + auto const end = inXs[pointIndex] + width; + auto const beginXIterator = + std::lower_bound(inXs.cbegin(), inXs.cend(), begin); + auto const endXIterator = std::lower_bound(inXs.cbegin(), inXs.cend(), end); + auto const pickSize = + static_cast<size_t>(std::distance(beginXIterator, endXIterator)); + if (pickSize > 0) { + auto const offset = + static_cast<size_t>(std::distance(inXs.cbegin(), beginXIterator)); + double xSum{0.}; + double ySum{0.}; + double eSquaredSum{0.}; + for (size_t pickIndex = offset; pickIndex < offset + pickSize; + ++pickIndex) { + xSum += inXs[pickIndex]; + ySum += inYs[pickIndex]; + eSquaredSum += pow<2>(inEs[pickIndex]); + } + outXs.emplace_back(xSum / static_cast<double>(pickSize)); + outYs.emplace_back(ySum / static_cast<double>(pickSize)); + outEs.emplace_back(std::sqrt(eSquaredSum) / + static_cast<double>(pickSize)); + auto const groupedXWidth = *std::prev(endXIterator) - *beginXIterator; + outDxs.emplace_back( + std::sqrt(pow<2>(inDxs[pointIndex]) + + pow<2>(FWHM_GAUSSIAN_EQUIVALENT * groupedXWidth))); + } else { + throw std::out_of_range( + "Failed to group. Is the X data sorted in ascending order?"); + } + begin = end; + if (begin > inXs.back()) { + break; + } + pointIndex += pickSize; + } + HistogramData::HistogramBuilder constructionYard; + constructionYard.setX(std::move(outXs)); + constructionYard.setY(std::move(outYs)); + constructionYard.setE(std::move(outEs)); + constructionYard.setDx(std::move(outDxs)); + constructionYard.setDistribution(false); + API::MatrixWorkspace_sptr outWS = + DataObjects::create<DataObjects::Workspace2D>(*inWS, + constructionYard.build()); + setProperty(Prop::OUTPUT_WS, outWS); +} + +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/Algorithms/test/GroupToXResolutionTest.h b/Framework/Algorithms/test/GroupToXResolutionTest.h new file mode 100644 index 0000000000000000000000000000000000000000..3e990f78fe020977597f6223f623e0e2792aea5f --- /dev/null +++ b/Framework/Algorithms/test/GroupToXResolutionTest.h @@ -0,0 +1,240 @@ +// 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_ALGORITHMS_GROUPTOXRESOLUTIONTEST_H_ +#define MANTID_ALGORITHMS_GROUPTOXRESOLUTIONTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAlgorithms/GroupToXResolution.h" + +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidHistogramData/LinearGenerator.h" +#include "MantidHistogramData/QuadraticGenerator.h" + +#include <boost/math/special_functions/pow.hpp> + +using namespace Mantid; +using boost::math::pow; + +class GroupToXResolutionTest : 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 GroupToXResolutionTest *createSuite() { + return new GroupToXResolutionTest(); + } + static void destroySuite(GroupToXResolutionTest *suite) { delete suite; } + + void test_Init() { + Algorithms::GroupToXResolution alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + } + + void test_single_point_remains_unchanged() { + HistogramData::Points Xs{0.23}; + HistogramData::Counts Ys{1.42}; + HistogramData::Histogram h(Xs, Ys); + API::MatrixWorkspace_sptr inputWS = + DataObjects::create<DataObjects::Workspace2D>(1, std::move(h)); + auto Dxs = Kernel::make_cow<HistogramData::HistogramDx>(1, 1.); + inputWS->setSharedDx(0, std::move(Dxs)); + Algorithms::GroupToXResolution 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("OutputWorkspace", "_unused_for_child")) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1) + TS_ASSERT_EQUALS(outputWS->blocksize(), 1) + TS_ASSERT_EQUALS(outputWS->x(0).front(), 0.23) + TS_ASSERT_EQUALS(outputWS->y(0).front(), 1.42) + TS_ASSERT_EQUALS(outputWS->e(0).front(), std::sqrt(1.42)) + TS_ASSERT(outputWS->hasDx(0)) + TS_ASSERT_EQUALS(outputWS->dx(0).front(), 1.) + } + + void test_two_points_get_averaged() { + HistogramData::Points Xs{0.2, 0.6}; + HistogramData::Counts Ys{1.5, 2.5}; + HistogramData::CountStandardDeviations Es{2., 3.}; + HistogramData::Histogram h(Xs, Ys, Es); + API::MatrixWorkspace_sptr inputWS = + DataObjects::create<DataObjects::Workspace2D>(1, std::move(h)); + auto Dxs = Kernel::make_cow<HistogramData::HistogramDx>(2); + { + auto &DxData = Dxs.access(); + DxData.front() = 1.2; + DxData.back() = 1.7; + } + inputWS->setSharedDx(0, std::move(Dxs)); + Algorithms::GroupToXResolution 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("OutputWorkspace", "_unused_for_child")) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("FractionOfDx", 1.)) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1) + TS_ASSERT_EQUALS(outputWS->blocksize(), 1) + TS_ASSERT_EQUALS(outputWS->x(0).front(), (0.2 + 0.6) / 2.) + TS_ASSERT_EQUALS(outputWS->y(0).front(), (1.5 + 2.5) / 2.) + TS_ASSERT_EQUALS(outputWS->e(0).front(), + std::sqrt(pow<2>(2.) + pow<2>(3.)) / 2.) + TS_ASSERT(outputWS->hasDx(0)) + TS_ASSERT_EQUALS(outputWS->dx(0).front(), + std::sqrt(pow<2>(1.2) + pow<2>(0.68 * (0.6 - 0.2)))) + } + + void test_two_spearate_points_remain_unchanged() { + HistogramData::Points Xs{0.2, 0.6}; + HistogramData::Counts Ys{1.5, 2.5}; + HistogramData::CountStandardDeviations Es{2., 3.}; + HistogramData::Histogram h(Xs, Ys, Es); + API::MatrixWorkspace_sptr inputWS = + DataObjects::create<DataObjects::Workspace2D>(1, std::move(h)); + auto Dxs = Kernel::make_cow<HistogramData::HistogramDx>(2); + { + auto &DxData = Dxs.access(); + DxData.front() = 0.1; + DxData.back() = 0.3; + } + inputWS->setSharedDx(0, std::move(Dxs)); + Algorithms::GroupToXResolution 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("OutputWorkspace", "_unused_for_child")) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("FractionOfDx", 1.)) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1) + TS_ASSERT_EQUALS(outputWS->blocksize(), 2) + TS_ASSERT_EQUALS(outputWS->x(0).front(), 0.2) + TS_ASSERT_EQUALS(outputWS->y(0).front(), 1.5) + TS_ASSERT_EQUALS(outputWS->e(0).front(), 2.) + TS_ASSERT(outputWS->hasDx(0)) + TS_ASSERT_EQUALS(outputWS->dx(0).front(), 0.1) + TS_ASSERT_EQUALS(outputWS->x(0).back(), 0.6) + TS_ASSERT_EQUALS(outputWS->y(0).back(), 2.5) + TS_ASSERT_EQUALS(outputWS->e(0).back(), 3.) + TS_ASSERT_EQUALS(outputWS->dx(0).back(), 0.3) + } + + void test_four_points_grouped_into_two() { + HistogramData::Points Xs{0.2, 0.6, 5.1, 5.7}; + HistogramData::Counts Ys{1.5, 2.5, -2.5, -1.5}; + HistogramData::CountStandardDeviations Es{2., 3., 2.5, 1.5}; + HistogramData::Histogram h(Xs, Ys, Es); + API::MatrixWorkspace_sptr inputWS = + DataObjects::create<DataObjects::Workspace2D>(1, std::move(h)); + auto Dxs = Kernel::make_cow<HistogramData::HistogramDx>(4); + { + auto &DxData = Dxs.access(); + DxData = {1., 0.1, 2., 0.2}; + } + inputWS->setSharedDx(0, std::move(Dxs)); + Algorithms::GroupToXResolution 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("OutputWorkspace", "_unused_for_child")) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("FractionOfDx", 1.)) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + API::MatrixWorkspace_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberHistograms(), 1) + TS_ASSERT_EQUALS(outputWS->blocksize(), 2) + TS_ASSERT_EQUALS(outputWS->x(0).front(), (0.2 + 0.6) / 2.) + TS_ASSERT_EQUALS(outputWS->y(0).front(), (1.5 + 2.5) / 2.) + TS_ASSERT_EQUALS(outputWS->e(0).front(), + std::sqrt(pow<2>(2.) + pow<2>(3.)) / 2.) + TS_ASSERT(outputWS->hasDx(0)) + TS_ASSERT_EQUALS(outputWS->dx(0).front(), + std::sqrt(pow<2>(1.) + pow<2>(0.68 * (0.6 - 0.2)))) + TS_ASSERT_EQUALS(outputWS->x(0).back(), (5.1 + 5.7) / 2.) + TS_ASSERT_EQUALS(outputWS->y(0).back(), (-2.5 + -1.5) / 2) + TS_ASSERT_EQUALS(outputWS->e(0).back(), + std::sqrt(pow<2>(2.5) + pow<2>(1.5)) / 2.) + TS_ASSERT_EQUALS(outputWS->dx(0).back(), + std::sqrt(pow<2>(2.) + pow<2>(0.68 * (5.7 - 5.1)))) + } +}; + +class GroupToXResolutionTestPerformance : 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 GroupToXResolutionTestPerformance *createSuite() { + return new GroupToXResolutionTestPerformance(); + } + static void destroySuite(GroupToXResolutionTestPerformance *suite) { + delete suite; + } + + GroupToXResolutionTestPerformance() : m_alg() { + m_alg.setRethrows(true); + m_alg.setChild(true); + m_alg.initialize(); + } + + void setUp() override { + constexpr double xZeroth{0.}; + constexpr double xFirst{0.}; + constexpr double xSecond{0.4}; + constexpr size_t n{10000}; + HistogramData::Points Xs( + n, HistogramData::QuadraticGenerator(xZeroth, xFirst, xSecond)); + HistogramData::Counts Ys(n, 1.3); + HistogramData::CountStandardDeviations Es(n, 1.1); + HistogramData::Histogram h(Xs, Ys, Es); + API::MatrixWorkspace_sptr inputWS = + DataObjects::create<DataObjects::Workspace2D>(1, std::move(h)); + // Construct DX such that in the beginning, we group multiple points + // and after a crossover, no grouping happens. + constexpr double initialGroupSize{10.}; + constexpr double crossover{0.8 * n}; + constexpr double dxZeroth{2. * initialGroupSize * xSecond}; + constexpr double dxFirst{(2. * crossover - 2. * initialGroupSize + 1.) / + crossover * xSecond}; + auto Dxs = Kernel::make_cow<HistogramData::HistogramDx>( + n, HistogramData::LinearGenerator(dxZeroth, dxFirst)); + inputWS->setSharedDx(0, std::move(Dxs)); + m_alg.setProperty("InputWorkspace", inputWS); + m_alg.setProperty("OutputWorkspace", "_out"); + m_alg.setProperty("FractionOfDx", 1.); + } + + void test_performance() { + for (int i = 0; i < 5000; ++i) { + TS_ASSERT_THROWS_NOTHING(m_alg.execute()) + } + } + +private: + Algorithms::GroupToXResolution m_alg; +}; + +#endif /* MANTID_ALGORITHMS_GROUPTOXRESOLUTIONTEST_H_ */ diff --git a/Framework/HistogramData/inc/MantidHistogramData/Histogram.h b/Framework/HistogramData/inc/MantidHistogramData/Histogram.h index 789c58738a79daa0651da76135d2e9f6ed441eaa..af0605e4aa9d69589c7599a6b7c1cd272c2ee900 100644 --- a/Framework/HistogramData/inc/MantidHistogramData/Histogram.h +++ b/Framework/HistogramData/inc/MantidHistogramData/Histogram.h @@ -197,8 +197,6 @@ private: template <class... T> bool selfAssignmentDx(const T &...) { return false; } template <class... T> bool selfAssignmentY(const T &...) { return false; } template <class... T> bool selfAssignmentE(const T &...) { return false; } - void switchDxToBinEdges(); - void switchDxToPoints(); XMode m_xMode; YMode m_yMode{YMode::Uninitialized}; diff --git a/docs/source/algorithms/GroupToXResolution-v1.rst b/docs/source/algorithms/GroupToXResolution-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..88afc513c9c77c31291ffbf77248da9c33b69af0 --- /dev/null +++ b/docs/source/algorithms/GroupToXResolution-v1.rst @@ -0,0 +1,62 @@ + +.. algorithm:: + +.. summary:: + +.. relatedalgorithms:: + +.. properties:: + +Description +----------- + +This algorithm groups the points of a single histogram workspace according to the X resolution stored in the DX array. + +The figure below shows schematically how the grouping procedure proceeds. + +.. figure:: ../images/GroupToXResolution_grouping_schema.png + :alt: Schematic image of the point grouping algorithm. + :scale: 100% + +#. Select first ungrouped point :math:`i`. The X resolution (DX) for this point is :math:`D_{i}`. +#. Calculate grouping width :math:`w_{i} =` ``FractionOfDx`` * :math:`D_{i}`. +#. Select ungrouped points within :math:`w_{i}` from point :math:`i`. +#. Calculate the average X, average Y, and the square root of the averaged squared sums of E of the selected points. +#. Calculate new resolution :math:`D^{\circ} = \sqrt{D_{i}^{2} + (0.68 \Delta X)^{2}}` where :math:`\Delta X` is span of the X values of the selected points. +#. Replace the selected points with a single grouped point. +#. Return to 1. + +Usage +----- + +**Example - Grouping points to X resolution** + +.. plot:: + :include-source: + + from mantid.simpleapi import CreateWorkspace, DeleteWorkspaces, GroupToXResolution + import matplotlib.pyplot as plt + import numpy as np + # Create a workspace with exponential decay. + Xs = np.arange(0.01, 5., 0.01) + Ys = np.exp(-Xs) + # A clumsy way for filling a numpy array. + # Numpy version > 1.7 would support 'DXs = full_like(Ys, 1.)' + DXs = np.empty_like(Ys) + DXs.fill(1.) + original = CreateWorkspace(Xs, Ys, Dx=DXs, NSpec=1) + grouped = GroupToXResolution(original) + # Plot side-by-side comparison. + fig, (left, right) = plt.subplots(ncols=2, subplot_kw={'projection':'mantid'}) + left.errorbar(original, linestyle='None') + left.set_title('Original') + right.errorbar(grouped, linestyle='None') + right.set_title('Grouped') + # Uncomment the next line to show the plot window. + #fig.show() + DeleteWorkspaces(['original', 'grouped']) + +.. categories:: + +.. sourcelink:: + diff --git a/docs/source/images/GroupToXResolution_grouping_schema.png b/docs/source/images/GroupToXResolution_grouping_schema.png new file mode 100644 index 0000000000000000000000000000000000000000..6f1df6d60bbebf1b1799af0c96d5539707b4dea0 Binary files /dev/null and b/docs/source/images/GroupToXResolution_grouping_schema.png differ diff --git a/docs/source/release/v3.14.0/reflectometry.rst b/docs/source/release/v3.14.0/reflectometry.rst index df356e4fc187062fe9677aa13b56fc4843a0c06f..b7473f39939dec96b5ce2b3de4c124eb42a0dc79 100644 --- a/docs/source/release/v3.14.0/reflectometry.rst +++ b/docs/source/release/v3.14.0/reflectometry.rst @@ -5,20 +5,6 @@ Reflectometry Changes .. contents:: Table of Contents :local: - -ISIS Reflectometry Interface ----------------------------- - -New -### - -* ``SaveReflectometryAscii`` is a general algorithm which saves the first spectrum of a workspace in Ascii format particularly suited for reflectometry data. - -Improvements -############ - -- The four Ascii save algorithms ``SaveANSTOAscii``, ``SaveILLCosmosAscii``, ``SaveReflCustomAscii`` and ``SaveReflThreeColumnAscii`` now correctly save x-error and can treat correctly point data and histograms. They are, however, deprecated in favour of ``SaveReflectometryAscii``. Please see ``SaveReflectometryAscii`` for more documentation. - Algorithms ---------- @@ -29,18 +15,31 @@ New - Added algorithm :ref:`algm-ApplyFloodWorkspace` which applies flood corrections to a workspace. - :ref:`FindReflectometryLines <algm-FindReflectometryLines-v2>` has been rewritten and updated to version 2. The new version finds a single line by a Gaussian fit. Version 1 has been deprecated and will be removed in a future release. - Added algorithm :ref:`algm-ReflectometrySliceEventWorkspace` which slices an input event workspace into multiple slices, producing a histogram workspace suitable for use with :ref:`algm-ReflectometryReductionOneAuto`. +- :ref:`algm-SaveReflectometryAscii` is a general algorithm which saves the first spectrum of a workspace in Ascii format particularly suited for reflectometry data. +- Some computations from :ref:`algm-ReflectometryMomentumTransfer` were extracted to a new algorithm, :ref:`algm-ReflectometryBeamStatistics`. +- :ref:`algm-GroupToXResolution` can be used to group the reflectivity data (as point data) to the :math`Q_z` resolution. Improved ######## - The ILL reduction workflow algorithms were reorganized to allow correct reflectivity calculation in the :literal:`SumInLambda` case. - Added flood corrections to :ref:`ReflectometryReductionOneAuto <algm-ReflectometryReductionOneAuto-v2>`. The correction data can be provided either via a flood workspace passed as a property or taken from the parameter file. +- The four Ascii save algorithms :ref:`algm-SaveANSTOAscii`, :ref:`algm-SaveILLCosmosAscii`, :ref:`algm-SaveReflCustomAscii` and :ref:`algm-SaveReflThreeColumnAscii` now correctly save x-error and can treat correctly point data and histograms. They are, however, deprecated in favour of :ref:`algm-SaveReflectometryAscii`. Please see :ref:`algm-SaveReflectometryAscii` for more documentation. +- :ref:`algm-ReflectometryReductionOneAuto` now supports the Wildes method for polarization corrections as well as Fredrikze when configured in the parameters file. +- :ref:`algm-ReflectometryReductionOne`, :ref:`algm-ReflectometryReductionOneAuto`, :ref:`algm-CreateTransmissionWorkspace` and :ref:`algm-CreateTransmissionWorkspaceAuto` now use spectrum numbers for their processing instructions instead of workspace indcies +- :ref:`algm-ReflectometryReductionOne` and :ref:`algm-ReflectometryReductionOneAuto` Now take a parameter to pass processing instructions to the transmission workspace algorithms and no longer accept strict spectrum checking +- Common naming of slit component name and size properties across algorithms. +- :ref:`algm-SpecularReflectionPositionCorrect` is now compatible with the reflectometers at ILL. +- :ref:`algm-CreateTransmissionWorkspace` and :ref:`algm-CreateTransmissionWorkspaceAuto` now use NormalizeByIntegratedMontitors instead of using MonitorIntegrationWavelengthMin and MonitorIntegrationWavelengthMax being defined, to determine how to normalize. Bug fixes ######### - Fixed the error propagation in :math:`Q` grouping in :ref:`ReflectometryILLConvertToQ <algm-ReflectometryILLConvertToQ>` - Handling of group workspaces containing single workspaces when scaling by period and using :literal:`ScaleFactorFromPeriod`, i.e. :literal:`UseManualScaleFactors` is true, :literal:`ManualScaleFactors` remains empty. +- A bug has been fixed on the Settings tab where the IncludePartialBins check box had been hidden by a misplaced text entry box. +- :ref:`algm-ReflectometryReductionOneAuto` No longer sums all of a transmission run's workspaces and instead will use the first run only +- In :ref:`algm-ReflectometryReductionOneAuto` an issue where if you gave only one of either MomentumTransferMax or MomentumTransferMin were specified it would be ignored, this has been fixed. Liquids Reflectometer --------------------- @@ -70,30 +69,4 @@ Bug fixes - The SaveASCII tab from the interface was unable to save in some places on Windows and that has now been fixed. -Algorithms ----------- - - -New -### - -- Some computations from :ref:`algm-ReflectometryMomentumTransfer` were extracted to a new algorithm, :ref:`algm-ReflectometryBeamStatistics`. - -Improved -######## - -- :ref:`algm-ReflectometryReductionOneAuto` now supports the Wildes method for polarization corrections as well as Fredrikze when configured in the parameters file. -- :ref:`algm-ReflectometryReductionOne`, :ref:`algm-ReflectometryReductionOneAuto`, :ref:`algm-CreateTransmissionWorkspace` and :ref:`algm-CreateTransmissionWorkspaceAuto` now use spectrum numbers for their processing instructions instead of workspace indcies -- :ref:`algm-ReflectometryReductionOne` and :ref:`algm-ReflectometryReductionOneAuto` Now take a parameter to pass processing instructions to the transmission workspace algorithms and no longer accept strict spectrum checking -- Common naming of slit component name and size properties across algorithms. -- :ref:`algm-SpecularReflectionPositionCorrect` is now compatible with the reflectometers at ILL. -- :ref:`algm-CreateTransmissionWorkspace` and :ref:`algm-CreateTransmissionWorkspaceAuto` now use NormalizeByIntegratedMontitors instead of using MonitorIntegrationWavelengthMin and MonitorIntegrationWavelengthMax being defined, to determine how to normalize. - -Bug fixes -######### - -- A bug has been fixed on the Settings tab where the IncludePartialBins check box had been hidden by a misplaced text entry box. -- :ref:`algm-ReflectometryReductionOneAuto` No longer sums all of a transmission run's workspaces and instead will use the first run only -- In :ref:`algm-ReflectometryReductionOneAuto` an issue where if you gave only one of either MomentumTransferMax or MomentumTransferMin were specified it would be ignored, this has been fixed. - :ref:`Release 3.14.0 <v3.14.0>`