diff --git a/Framework/API/CMakeLists.txt b/Framework/API/CMakeLists.txt index 5afd011c1e9098f940964d4f6c9d9d7cc0e8d749..3fa7bdf8ff6ad8ad41ae13c977defe2ce4c8d3d3 100644 --- a/Framework/API/CMakeLists.txt +++ b/Framework/API/CMakeLists.txt @@ -96,6 +96,7 @@ set ( SRC_FILES src/LogManager.cpp src/LogarithmScale.cpp src/MDGeometry.cpp + src/MDFrameValidator.cpp src/MatrixWorkspace.cpp src/MatrixWorkspaceMDIterator.cpp src/ModeratorModel.cpp @@ -292,6 +293,7 @@ set ( INC_FILES inc/MantidAPI/LogManager.h inc/MantidAPI/LogarithmScale.h inc/MantidAPI/MDGeometry.h + inc/MantidAPI/MDFrameValidator.h inc/MantidAPI/MatrixWorkspace.h inc/MantidAPI/MatrixWorkspaceMDIterator.h inc/MantidAPI/MatrixWorkspaceValidator.h @@ -420,6 +422,7 @@ set ( TEST_FILES LogFilterGeneratorTest.h LogManagerTest.h MDGeometryTest.h + MDFrameValidatorTest.h MatrixWorkspaceMDIteratorTest.h ModeratorModelTest.h MuParserUtilsTest.h diff --git a/Framework/API/inc/MantidAPI/MDFrameValidator.h b/Framework/API/inc/MantidAPI/MDFrameValidator.h new file mode 100644 index 0000000000000000000000000000000000000000..f34488e7f7397347bbbae2b92ab7b338cc622e6c --- /dev/null +++ b/Framework/API/inc/MantidAPI/MDFrameValidator.h @@ -0,0 +1,55 @@ +#ifndef MANTID_API_MDFRAMEVALIDATOR_H +#define MANTID_API_MDFRAMEVALIDATOR_H + +#include "MantidAPI/DllConfig.h" +#include "MantidAPI/IMDWorkspace.h" +#include "MantidKernel/TypedValidator.h" + +/** + A validator which checks that the frame of the MDWorkspace referred to + by a WorkspaceProperty is the expected one. + + Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +namespace Mantid { +namespace API { +class MANTID_API_DLL MDFrameValidator + : public Kernel::TypedValidator<IMDWorkspace_sptr> { +public: + explicit MDFrameValidator(const std::string &frameName); + /// Gets the type of the validator + std::string getType() const { return "mdframe"; } + /// Clone the current state + Kernel::IValidator_sptr clone() const override; + +private: + /// Check for validity. + std::string checkValidity(const IMDWorkspace_sptr &workspace) const override; + + /// The name of the required frame + const std::string m_frameID; +}; + +} // namespace API +} // namespace Mantid + +#endif // MANTID_API_MDFRAMEVALIDATOR_H diff --git a/Framework/API/src/MDFrameValidator.cpp b/Framework/API/src/MDFrameValidator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2165a927cf9cdae71d943995e0e230475f9166dd --- /dev/null +++ b/Framework/API/src/MDFrameValidator.cpp @@ -0,0 +1,42 @@ +#include "MantidAPI/MDFrameValidator.h" +#include "MantidKernel/IValidator.h" +#include <boost/make_shared.hpp> + +using Mantid::Kernel::IValidator_sptr; + +namespace Mantid { +namespace API { + +/** Constructor + * + * @param frameName :: The name of the frame that the workspace must have. + */ +MDFrameValidator::MDFrameValidator(const std::string &frameName) + : m_frameID{frameName} {} + +/** + * Clone the current state + */ +Kernel::IValidator_sptr MDFrameValidator::clone() const { + return boost::make_shared<MDFrameValidator>(*this); +} + +/** Checks that the frame of the MDWorkspace matches the expected frame. + * + * @param workspace :: The workspace to test + * @return A user level description of the error or "" for no error + */ +std::string +MDFrameValidator::checkValidity(const IMDWorkspace_sptr &workspace) const { + + for (size_t index = 0; index < workspace->getNumDims(); ++index) { + const auto &frame = workspace->getDimension(index)->getMDFrame(); + if (frame.name() != m_frameID) + return "MDWorkspace must be in the " + m_frameID + " frame."; + } + + return ""; +} + +} // namespace API +} // namespace Mantid diff --git a/Framework/API/test/MDFrameValidatorTest.h b/Framework/API/test/MDFrameValidatorTest.h new file mode 100644 index 0000000000000000000000000000000000000000..2896ae18367965499ad116dfd2045d6b9dbdbdde --- /dev/null +++ b/Framework/API/test/MDFrameValidatorTest.h @@ -0,0 +1,76 @@ +#ifndef MANTID_MDUNITVALIDATOR_TEST_H +#define MANTID_MDUNITVALIDATOR_TEST_H + +#include <boost/make_shared.hpp> +#include <cxxtest/TestSuite.h> + +#include "MantidAPI/IMDEventWorkspace_fwd.h" +#include "MantidAPI/MDFrameValidator.h" +#include "MantidGeometry/MDGeometry/HKL.h" +#include "MantidGeometry/MDGeometry/MDFrameFactory.h" +#include "MantidGeometry/MDGeometry/MDHistoDimension.h" +#include "MantidGeometry/MDGeometry/QLab.h" +#include "MantidKernel/UnitLabelTypes.h" +#include "MantidTestHelpers/FakeObjects.h" + +using namespace Mantid::Geometry; +using namespace Mantid::API; +using namespace Mantid::Kernel; + +class MDFrameValidatorTest : 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 MDFrameValidatorTest *createSuite() { + return new MDFrameValidatorTest(); + } + static void destroySuite(MDFrameValidatorTest *suite) { delete suite; } + + void testGetType() { + MDFrameValidator unitValidator(HKL::HKLName); + TS_ASSERT_EQUALS(unitValidator.getType(), "mdframe"); + } + + void testHKLMDWorkspaceIsValidForValidatorWithHKLFrame() { + MDFrameValidator frameValidator(HKL::HKLName); + + HKLFrameFactory factory; + auto frame = + factory.create(MDFrameArgument{HKL::HKLName, Units::Symbol::RLU}); + auto dim = boost::make_shared<MDHistoDimension>("x", "x", *frame, 0.0f, + 100.0f, 10); + auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim, dim, dim); + TS_ASSERT_EQUALS(frameValidator.isValid(ws), "") + }; + + void testHKLMDWorkspaceIsNotValidForValidatorWithQLabFrame() { + MDFrameValidator frameValidator(QLab::QLabName); + + MDFrameArgument args{HKL::HKLName, Units::Symbol::RLU}; + auto frame = HKLFrameFactory().create(args); + auto dim = boost::make_shared<MDHistoDimension>("x", "x", *frame, 0.0f, + 100.0f, 10); + auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim, dim, dim); + TS_ASSERT_EQUALS(frameValidator.isValid(ws), + "MDWorkspace must be in the " + QLab::QLabName + " frame.") + }; + + void testMixedAxisMDWorkspaceIsNotValidForValidatorWithQLabFrame() { + MDFrameValidator frameValidator(QLab::QLabName); + + MDFrameArgument axisArgs1{HKL::HKLName, Units::Symbol::RLU}; + MDFrameArgument axisArgs2{QLab::QLabName, Units::Symbol::InverseAngstrom}; + + auto frame1 = HKLFrameFactory().create(axisArgs1); + auto frame2 = QLabFrameFactory().create(axisArgs2); + auto dim1 = boost::make_shared<MDHistoDimension>("x", "x", *frame1, 0.0f, + 100.0f, 10); + auto dim2 = boost::make_shared<MDHistoDimension>("x", "x", *frame1, 0.0f, + 100.0f, 10); + auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim1, dim2, dim2); + TS_ASSERT_EQUALS(frameValidator.isValid(ws), + "MDWorkspace must be in the " + QLab::QLabName + " frame.") + }; +}; + +#endif // MANTID_MDUNITVALIDATOR_TEST_H diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt index 878ad4bb5316b6f0b4745bf59294970143cd6e3b..78c1b6bb98747d4cfc6e3da3e3d1a4ad770ab00c 100644 --- a/Framework/Kernel/CMakeLists.txt +++ b/Framework/Kernel/CMakeLists.txt @@ -219,7 +219,6 @@ set ( INC_FILES inc/MantidKernel/Logger.h inc/MantidKernel/MDAxisValidator.h inc/MantidKernel/MDUnit.h - inc/MantidKernel/MDUnitFactory.h inc/MantidKernel/MRUList.h inc/MantidKernel/MagneticFormFactorTable.h inc/MantidKernel/MagneticIon.h diff --git a/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp b/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp index 898b1032bfd0a414f110e83b1296a5cd85837d87..52bbdbae877ee847ad4e1dcc761a4d36b23d435d 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp @@ -1,5 +1,7 @@ #include "MantidAPI/CommonBinsValidator.h" #include "MantidAPI/HistogramValidator.h" +#include "MantidAPI/IMDWorkspace.h" +#include "MantidAPI/MDFrameValidator.h" #include "MantidAPI/NumericAxisValidator.h" #include "MantidAPI/RawCountValidator.h" #include "MantidAPI/SpectraAxisValidator.h" @@ -7,6 +9,7 @@ #include "MantidPythonInterface/kernel/TypedValidatorExporter.h" #include <boost/python/class.hpp> +using Mantid::API::IMDWorkspace_sptr; using Mantid::Kernel::TypedValidator; using Mantid::PythonInterface::TypedValidatorExporter; using namespace boost::python; @@ -17,9 +20,13 @@ void export_MatrixWorkspaceValidator() { using Mantid::API::MatrixWorkspaceValidator; TypedValidatorExporter<MatrixWorkspace_sptr>::define( "MatrixWorkspaceValidator"); + TypedValidatorExporter<IMDWorkspace_sptr>::define("IMDWorkspaceValidator"); class_<MatrixWorkspaceValidator, bases<TypedValidator<MatrixWorkspace_sptr>>, boost::noncopyable>("MatrixWorkspaceValidator", no_init); + + class_<TypedValidator<IMDWorkspace_sptr>, boost::noncopyable>( + "IMDWorkspaceValidator", no_init); } /// Export a validator derived from a MatrixWorkspaceValidator that has a no-arg /// constructor @@ -62,4 +69,14 @@ void export_WorkspaceValidators() { EXPORT_WKSP_VALIDATOR_DEFAULT_ARG( NumericAxisValidator, int, "axisNumber", 1, "Checks whether the axis specified by axisNumber is a NumericAxis"); + + class_<MDFrameValidator, bases<TypedValidator<IMDWorkspace_sptr>>, + boost::noncopyable>( + "MDFrameValidator", + init<std::string>(arg("frameName"), + "Checks the MD workspace has the given frame along all " + "dimensions. Accepted values for the `frameName` are " + "currently: `HKL`, `QLab`, `QSample`, `Time of " + "Flight`, `Distance`, `General frame`, `Unknown " + "frame` ")); } diff --git a/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py b/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py index f85d754b4681c794cbc49f4f362c4c7f0bb8694a..128b3f48ddfcc8310219d4063d38703a0e47eded 100644 --- a/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py +++ b/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py @@ -9,7 +9,7 @@ from mantid.kernel import IValidator from mantid.api import (WorkspaceUnitValidator, HistogramValidator, RawCountValidator, CommonBinsValidator, SpectraAxisValidator, NumericAxisValidator, - InstrumentValidator) + InstrumentValidator, MDFrameValidator) class WorkspaceValidatorsTest(unittest.TestCase): @@ -67,5 +67,10 @@ class WorkspaceValidatorsTest(unittest.TestCase): """ testhelpers.assertRaisesNothing(self, InstrumentValidator) + def test_MDFrameValidator_construction(self): + testhelpers.assertRaisesNothing(self, MDFrameValidator, "HKL") + self.assertRaises(Exception, MDFrameValidator) + + if __name__ == '__main__': unittest.main() diff --git a/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h b/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h index 542ec7189b3dedc7f846f96e792ca1fd98073553..6e69adec50387d9dcb9e66aa7b6a9833621d81ec 100644 --- a/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h +++ b/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h @@ -22,6 +22,7 @@ #include <map> #include <string> +#include "MantidAPI/IMDHistoWorkspace.h" #include "MantidAPI/ISpectrum.h" #include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/MatrixWorkspace.h" @@ -29,12 +30,18 @@ #include "MantidAPI/SpectraAxis.h" #include "MantidGeometry/Instrument.h" #include "MantidGeometry/Instrument/DetectorGroup.h" +#include "MantidGeometry/MDGeometry/MDHistoDimension.h" +#include "MantidKernel/SpecialCoordinateSystem.h" #include "MantidKernel/cow_ptr.h" using namespace Mantid::API; +using namespace Mantid::Geometry; +using Mantid::Kernel::SpecialCoordinateSystem; +using Mantid::coord_t; using Mantid::detid_t; -using Mantid::specnum_t; using Mantid::MantidVec; +using Mantid::signal_t; +using Mantid::specnum_t; //=================================================================================================================== /** Helper class that implements ISpectrum */ @@ -412,6 +419,308 @@ protected: } }; +//=================================================================================================================== +class MDHistoWorkspaceTester : public IMDHistoWorkspace { + +public: + uint64_t getNPoints() const override { + throw std::runtime_error("Not Implemented"); + } + uint64_t getNEvents() const override { + throw std::runtime_error("Not Implemented"); + } + + std::vector<std::unique_ptr<IMDIterator>> createIterators( + size_t suggestedNumCores = 1, + Mantid::Geometry::MDImplicitFunction *function = nullptr) const override { + UNUSED_ARG(suggestedNumCores) + UNUSED_ARG(function) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalAtCoord( + const coord_t *coords, + const Mantid::API::MDNormalization &normalization) const override { + UNUSED_ARG(coords); + UNUSED_ARG(normalization); + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalWithMaskAtCoord( + const coord_t *coords, + const Mantid::API::MDNormalization &normalization) const override { + UNUSED_ARG(coords); + UNUSED_ARG(normalization); + throw std::runtime_error("Not Implemented"); + } + + void + setMDMasking(Mantid::Geometry::MDImplicitFunction *maskingRegion) override { + UNUSED_ARG(maskingRegion); + throw std::runtime_error("Not Implemented"); + } + + void clearMDMasking() override { + throw std::runtime_error("Not Implemented"); + } + + SpecialCoordinateSystem getSpecialCoordinateSystem() const override { + throw std::runtime_error("Not Implemented"); + } + + coord_t getInverseVolume() const override { + throw std::runtime_error("Not Implemented"); + } + + signal_t *getSignalArray() const override { + throw std::runtime_error("Not Implemented"); + } + + signal_t *getErrorSquaredArray() const override { + throw std::runtime_error("Not Implemented"); + } + + signal_t *getNumEventsArray() const override { + throw std::runtime_error("Not Implemented"); + } + + void setTo(signal_t signal, signal_t errorSquared, + signal_t numEvents) override { + UNUSED_ARG(signal); + UNUSED_ARG(errorSquared); + UNUSED_ARG(numEvents); + throw std::runtime_error("Not Implemented"); + } + + Mantid::Kernel::VMD getCenter(size_t linearIndex) const override { + UNUSED_ARG(linearIndex); + throw std::runtime_error("Not Implemented"); + } + + void setSignalAt(size_t index, signal_t value) override { + UNUSED_ARG(index) + UNUSED_ARG(value) + throw std::runtime_error("Not Implemented"); + } + + void setErrorSquaredAt(size_t index, signal_t value) override { + UNUSED_ARG(index) + UNUSED_ARG(value) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorAt(size_t index) const override { + UNUSED_ARG(index) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorAt(size_t index1, size_t index2) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorAt(size_t index1, size_t index2, + size_t index3) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorAt(size_t index1, size_t index2, size_t index3, + size_t index4) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + UNUSED_ARG(index4) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalAt(size_t index) const override { + UNUSED_ARG(index) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalAt(size_t index1, size_t index2) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalAt(size_t index1, size_t index2, + size_t index3) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalAt(size_t index1, size_t index2, size_t index3, + size_t index4) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + UNUSED_ARG(index4) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalNormalizedAt(size_t index) const override { + UNUSED_ARG(index) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalNormalizedAt(size_t index1, size_t index2) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalNormalizedAt(size_t index1, size_t index2, + size_t index3) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + throw std::runtime_error("Not Implemented"); + } + + signal_t getSignalNormalizedAt(size_t index1, size_t index2, size_t index3, + size_t index4) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + UNUSED_ARG(index4) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorNormalizedAt(size_t index) const override { + UNUSED_ARG(index) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorNormalizedAt(size_t index1, size_t index2) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorNormalizedAt(size_t index1, size_t index2, + size_t index3) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + throw std::runtime_error("Not Implemented"); + } + + signal_t getErrorNormalizedAt(size_t index1, size_t index2, size_t index3, + size_t index4) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + UNUSED_ARG(index4) + throw std::runtime_error("Not Implemented"); + } + + signal_t &errorSquaredAt(size_t index) override { + UNUSED_ARG(index) + throw std::runtime_error("Not Implemented"); + } + + signal_t &signalAt(size_t index) override { + UNUSED_ARG(index) + throw std::runtime_error("Not Implemented"); + } + + size_t getLinearIndex(size_t index1, size_t index2) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + throw std::runtime_error("Not Implemented"); + } + + size_t getLinearIndex(size_t index1, size_t index2, + size_t index3) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + throw std::runtime_error("Not Implemented"); + } + + size_t getLinearIndex(size_t index1, size_t index2, size_t index3, + size_t index4) const override { + UNUSED_ARG(index1) + UNUSED_ARG(index2) + UNUSED_ARG(index3) + UNUSED_ARG(index4) + throw std::runtime_error("Not Implemented"); + } + + LinePlot getLineData(const Mantid::Kernel::VMD &start, + const Mantid::Kernel::VMD &end, + Mantid::API::MDNormalization normalize) const override { + UNUSED_ARG(start) + UNUSED_ARG(end) + UNUSED_ARG(normalize) + throw std::runtime_error("Not Implemented"); + } + + double &operator[](const size_t &index)override { + UNUSED_ARG(index) + throw std::runtime_error("Not Implemented"); + } + + void + setCoordinateSystem(const SpecialCoordinateSystem coordinateSystem) override { + UNUSED_ARG(coordinateSystem) + throw std::runtime_error("Not Implemented"); + } + + void setDisplayNormalization( + const Mantid::API::MDNormalization &preferredNormalization) override { + UNUSED_ARG(preferredNormalization); + throw std::runtime_error("Not Implemented"); + } + + // Check if this class has an oriented lattice on any sample object + bool hasOrientedLattice() const override { + return MultipleExperimentInfos::hasOrientedLattice(); + } + + size_t getMemorySize() const override { + throw std::runtime_error("Not Implemented"); + } + + const std::string id() const override { + + throw std::runtime_error("Not Implemented"); + } + const std::string &getName() const override { + + throw std::runtime_error("Not Implemented"); + } + bool threadSafe() const override { + + throw std::runtime_error("Not Implemented"); + } + const std::string toString() const override { + + throw std::runtime_error("Not Implemented"); + } + MDHistoWorkspaceTester(MDHistoDimension_sptr dimX, MDHistoDimension_sptr dimY, + MDHistoDimension_sptr dimZ) { + std::vector<IMDDimension_sptr> dimensions{dimX, dimY, dimZ}; + initGeometry(dimensions); + } + +private: + IMDHistoWorkspace *doClone() const override { + throw std::runtime_error("Not Implemented"); + } + IMDHistoWorkspace *doCloneEmpty() const override { + + throw std::runtime_error("Not Implemented"); + } +}; + class VariableBinThrowingTester : public AxeslessWorkspaceTester { size_t blocksize() const override { if (getSpectrum(0).dataY().size() == getSpectrum(1).dataY().size()) diff --git a/docs/source/release/v3.13.0/framework.rst b/docs/source/release/v3.13.0/framework.rst index 304588e8554a5d522aa0234b22e79a0a4281b6c2..cadb92ac1d4999a2dbd5aefb3ae7de09052b1825 100644 --- a/docs/source/release/v3.13.0/framework.rst +++ b/docs/source/release/v3.13.0/framework.rst @@ -76,6 +76,11 @@ New Python ------ +New +### + +- Added a new ``MDFrameValidator`` which can check that a MD workspace passed to a python algorithm has the expected MD frame (e.g. HKL, QLab, QSample etc.). + Improved ########