diff --git a/Framework/Algorithms/src/CorrectKiKf.cpp b/Framework/Algorithms/src/CorrectKiKf.cpp index e4eeb280e902cd704b760b930ffb9ccb9ab0a944..9cc3b2dc48b7a40598a1162f38dda94193f1a6c8 100644 --- a/Framework/Algorithms/src/CorrectKiKf.cpp +++ b/Framework/Algorithms/src/CorrectKiKf.cpp @@ -67,9 +67,9 @@ void CorrectKiKf::exec() { } // If input and output workspaces are not the same, create a new workspace for - // the output + // the output using the clone method to account for different workspace types if (outputWS != inputWS) { - outputWS = create<MatrixWorkspace>(*inputWS); + outputWS = inputWS->clone(); } const size_t size = inputWS->blocksize(); diff --git a/Framework/Algorithms/src/Rebin2D.cpp b/Framework/Algorithms/src/Rebin2D.cpp index 1d0d922a26f74d18dd454224a7d9f0f5a4404ac5..8301c3292612451ade37a55d912988c65bc9c984 100644 --- a/Framework/Algorithms/src/Rebin2D.cpp +++ b/Framework/Algorithms/src/Rebin2D.cpp @@ -155,7 +155,8 @@ void Rebin2D::exec() { } PARALLEL_CHECK_INTERUPT_REGION if (useFractionalArea) { - outputRB->finalize(true, true); + FractionalRebinning::finalizeFractionalRebin(*outputRB); + outputRB->finalize(true); } FractionalRebinning::normaliseOutput(outputWS, inputWS, m_progress.get()); diff --git a/Framework/Algorithms/src/SofQWNormalisedPolygon.cpp b/Framework/Algorithms/src/SofQWNormalisedPolygon.cpp index 56e9482ab0b39815ce80aedac63badc5d77aea32..e18a314c97e1b38a95457d50097c9c14cff13dde 100644 --- a/Framework/Algorithms/src/SofQWNormalisedPolygon.cpp +++ b/Framework/Algorithms/src/SofQWNormalisedPolygon.cpp @@ -461,6 +461,7 @@ void SofQWNormalisedPolygon::exec() { } PARALLEL_CHECK_INTERUPT_REGION + FractionalRebinning::finalizeFractionalRebin(*outputWS); outputWS->finalize(); FractionalRebinning::normaliseOutput(outputWS, inputWS, m_progress.get()); diff --git a/Framework/Algorithms/src/SumSpectra.cpp b/Framework/Algorithms/src/SumSpectra.cpp index 856e4d87864b6ce0e21a826cae29642cf03dfd28..709247612b0812a9834b8ba8a3ef1cc755b808dc 100644 --- a/Framework/Algorithms/src/SumSpectra.cpp +++ b/Framework/Algorithms/src/SumSpectra.cpp @@ -165,6 +165,10 @@ void SumSpectra::init() { setPropertySettings( "MultiplyBySpectra", std::make_unique<EnabledWhenProperty>("WeightedSum", IS_EQUAL_TO, "1")); + + declareProperty("UseFractionalArea", true, + "Normalize the output workspace to the fractional area for " + "RebinnedOutput workspaces."); } /* @@ -520,6 +524,10 @@ void SumSpectra::doFractionalSum(MatrixWorkspace_sptr outputWorkspace, RebinnedOutput_sptr outWS = boost::dynamic_pointer_cast<RebinnedOutput>(outputWorkspace); + // Check finalize state prior to the sum process, at the completion + // the output is unfinalized + auto isFinalized = inWS->isFinalized(); + // Get references to the output workspaces's data vectors auto &outSpec = outputWorkspace->getSpectrum(0); auto &YSum = outSpec.mutableY(); @@ -548,21 +556,22 @@ void SumSpectra::doFractionalSum(MatrixWorkspace_sptr outputWorkspace, if (m_calculateWeightedSum) { for (size_t yIndex = 0; yIndex < m_yLength; ++yIndex) { const double yErrorsVal = YErrors[yIndex]; + const double fracVal = (isFinalized ? FracArea[yIndex] : 1.0); if (std::isnormal(yErrorsVal)) { // is non-zero, nan, or infinity - const double errsq = - yErrorsVal * yErrorsVal * FracArea[yIndex] * FracArea[yIndex]; + const double errsq = yErrorsVal * yErrorsVal * fracVal * fracVal; YErrorSum[yIndex] += errsq; Weight[yIndex] += 1. / errsq; - YSum[yIndex] += YValues[yIndex] * FracArea[yIndex] / errsq; + YSum[yIndex] += YValues[yIndex] * fracVal / errsq; } else { nZeros[yIndex]++; } } } else { for (size_t yIndex = 0; yIndex < m_yLength; ++yIndex) { - YSum[yIndex] += YValues[yIndex] * FracArea[yIndex]; - YErrorSum[yIndex] += YErrors[yIndex] * YErrors[yIndex] * - FracArea[yIndex] * FracArea[yIndex]; + const double fracVal = (isFinalized ? FracArea[yIndex] : 1.0); + YSum[yIndex] += YValues[yIndex] * fracVal; + YErrorSum[yIndex] += + YErrors[yIndex] * YErrors[yIndex] * fracVal * fracVal; } } // accumulation of fractional weight is the same @@ -583,8 +592,11 @@ void SumSpectra::doFractionalSum(MatrixWorkspace_sptr outputWorkspace, numZeros = 0; } - // Create the correct representation - outWS->finalize(); + // Create the correct representation if using fractional area + auto useFractionalArea = getProperty("UseFractionalArea"); + if (useFractionalArea) { + outWS->finalize(); + } } /** Executes the algorithm diff --git a/Framework/Algorithms/test/SumSpectraTest.h b/Framework/Algorithms/test/SumSpectraTest.h index 9b788614fb39d0b4a404da52ca29c360f67f5308..4f6b59606eb21cdea1cc6e2410d79552b32f68fa 100644 --- a/Framework/Algorithms/test/SumSpectraTest.h +++ b/Framework/Algorithms/test/SumSpectraTest.h @@ -342,28 +342,52 @@ public: RebinnedOutput_sptr output; output = AnalysisDataService::Instance().retrieveWS<RebinnedOutput>(outName); - TS_ASSERT(output); - TS_ASSERT_EQUALS(output->getNumberHistograms(), 1); - TS_ASSERT_EQUALS(output->blocksize(), 6); - // Row with full acceptance - TS_ASSERT_EQUALS(output->y(0)[1], 1.); - TS_ASSERT_DELTA(output->e(0)[1], 0.40824829046386296, 1.e-5); - TS_ASSERT_EQUALS(output->dataF(0)[1], 6.); - // Row with limited, but non-zero acceptance, shouldn't have nans! - TS_ASSERT_DELTA(output->y(0)[5], 0.66666, 1.e-5); - TS_ASSERT_DELTA(output->e(0)[5], 0.47140452079103173, 1.e-5); - TS_ASSERT_EQUALS(output->dataF(0)[5], 3.); - TS_ASSERT(output->run().hasProperty("NumAllSpectra")) - TS_ASSERT(output->run().hasProperty("NumMaskSpectra")) - TS_ASSERT(output->run().hasProperty("NumZeroSpectra")) - - TS_ASSERT_EQUALS(boost::lexical_cast<std::string>(nHist), - output->run().getLogData("NumAllSpectra")->value()) - TS_ASSERT_EQUALS(boost::lexical_cast<std::string>(0), - output->run().getLogData("NumMaskSpectra")->value()) - TS_ASSERT_EQUALS(boost::lexical_cast<std::string>(0), - output->run().getLogData("NumZeroSpectra")->value()) + auto evaluate = [&, nHist](RebinnedOutput_sptr &output) { + TS_ASSERT(output); + TS_ASSERT_EQUALS(output->getNumberHistograms(), 1); + TS_ASSERT_EQUALS(output->blocksize(), 6); + // Row with full acceptance + TS_ASSERT_EQUALS(output->y(0)[1], 1.); + TS_ASSERT_DELTA(output->e(0)[1], 0.40824829046386296, 1.e-5); + TS_ASSERT_EQUALS(output->dataF(0)[1], 6.); + // Row with limited, but non-zero acceptance, shouldn't have nans! + TS_ASSERT_DELTA(output->y(0)[5], 0.66666, 1.e-5); + TS_ASSERT_DELTA(output->e(0)[5], 0.47140452079103173, 1.e-5); + TS_ASSERT_EQUALS(output->dataF(0)[5], 3.); + + TS_ASSERT(output->run().hasProperty("NumAllSpectra")) + TS_ASSERT(output->run().hasProperty("NumMaskSpectra")) + TS_ASSERT(output->run().hasProperty("NumZeroSpectra")) + + TS_ASSERT_EQUALS(boost::lexical_cast<std::string>(nHist), + output->run().getLogData("NumAllSpectra")->value()) + TS_ASSERT_EQUALS(boost::lexical_cast<std::string>(0), + output->run().getLogData("NumMaskSpectra")->value()) + TS_ASSERT_EQUALS(boost::lexical_cast<std::string>(0), + output->run().getLogData("NumZeroSpectra")->value()) + }; + + evaluate(output); + + // unfinalize the workspace and confirm it gives the same results + // using a clean algorithm + ws->unfinalize(); + AnalysisDataService::Instance().addOrReplace(inName, ws); + Mantid::Algorithms::SumSpectra alg3a; + if (!alg3a.isInitialized()) { + alg3a.initialize(); + } + // Set the properties + alg3a.setPropertyValue("InputWorkspace", inName); + alg3a.setPropertyValue("OutputWorkspace", outName); + alg3a.setProperty("IncludeMonitors", false); + alg3a.setProperty("RemoveSpecialValues", true); + alg3a.execute(); + TS_ASSERT(alg3a.isExecuted()); + output = + AnalysisDataService::Instance().retrieveWS<RebinnedOutput>(outName); + evaluate(output); } void testExecNoLimitsWeighted() { Mantid::Algorithms::SumSpectra alg2; diff --git a/Framework/DataHandling/src/LoadNexusProcessed.cpp b/Framework/DataHandling/src/LoadNexusProcessed.cpp index e251aea1bb830fc4d0c679526c2393eebaff14be..f22a90ae90b0b7ea5ded8acc6a8a874032817d2e 100644 --- a/Framework/DataHandling/src/LoadNexusProcessed.cpp +++ b/Framework/DataHandling/src/LoadNexusProcessed.cpp @@ -1302,6 +1302,16 @@ API::MatrixWorkspace_sptr LoadNexusProcessed::loadNonEventEntry( NXDataSetTyped<double> fracarea = errors; if (hasFracArea) { fracarea = wksp_cls.openNXDouble("frac_area"); + + // Set the fractional area attributes, default values consistent with + // previous assumptions: finalized = true, sqrdErrs = false + auto rbWS = boost::dynamic_pointer_cast<RebinnedOutput>(local_workspace); + auto finalizedValue = fracarea.attributes("finalized"); + auto finalized = (finalizedValue.empty() ? true : finalizedValue == "1"); + rbWS->setFinalized(finalized); + auto sqrdErrsValue = fracarea.attributes("sqrd_errors"); + auto sqrdErrs = (sqrdErrsValue.empty() ? false : sqrdErrsValue == "1"); + rbWS->setSqrdErrors(sqrdErrs); } // Check for x errors; as with fracArea we set it to xbins diff --git a/Framework/DataObjects/inc/MantidDataObjects/FractionalRebinning.h b/Framework/DataObjects/inc/MantidDataObjects/FractionalRebinning.h index eaa08f04d077365c80a85291dce65708ec6c755f..6bf685879efb01964daafd16d8628848d658894f 100644 --- a/Framework/DataObjects/inc/MantidDataObjects/FractionalRebinning.h +++ b/Framework/DataObjects/inc/MantidDataObjects/FractionalRebinning.h @@ -62,6 +62,10 @@ MANTID_DATAOBJECTS_DLL void rebinToFractionalOutput( const std::vector<double> &verticalAxis, const DataObjects::RebinnedOutput_const_sptr &inputRB = nullptr); +/// Set finalize flag after fractional rebinning loop +MANTID_DATAOBJECTS_DLL void +finalizeFractionalRebin(DataObjects::RebinnedOutput &outputWS); + } // namespace FractionalRebinning } // namespace DataObjects diff --git a/Framework/DataObjects/inc/MantidDataObjects/RebinnedOutput.h b/Framework/DataObjects/inc/MantidDataObjects/RebinnedOutput.h index 28ba488a3b6aa53a96265beda56ede5a61d307c6..4335a7c7d0fb4a6d9133415e45220b9cf625508e 100644 --- a/Framework/DataObjects/inc/MantidDataObjects/RebinnedOutput.h +++ b/Framework/DataObjects/inc/MantidDataObjects/RebinnedOutput.h @@ -25,8 +25,9 @@ namespace DataObjects { */ class DLLExport RebinnedOutput : public Workspace2D { public: - RebinnedOutput() : m_finalized(false) {} - RebinnedOutput(bool finalized) : m_finalized(finalized) {} + RebinnedOutput() : m_finalized(false), m_hasSqrdErrs(true) {} + RebinnedOutput(bool finalized, bool hasSqrdErrs) + : m_finalized(finalized), m_hasSqrdErrs(hasSqrdErrs) {} /// Returns a clone of the workspace std::unique_ptr<RebinnedOutput> clone() const { return std::unique_ptr<RebinnedOutput>(doClone()); @@ -46,18 +47,30 @@ public: /// Returns the fractional area virtual const MantidVec &dataF(const std::size_t index) const; - /// Create final representation - void finalize(bool hasSqrdErrs = true, bool force = false); - void unfinalize(bool hasSqrdErrs = false, bool force = false); + /// Finalize to fractional area representation + void finalize(bool hasSqrdErrs = true); + /// Undo fractional area representation + void unfinalize(); /// Returns if finalize has been called bool isFinalized() const { return m_finalized; } + /// Override the finalized flag + void setFinalized(bool value) { m_finalized = value; } + + /// Returns if using squared errors + bool hasSqrdErrors() const { return m_hasSqrdErrs; } + /// Override the squared errors flag + void setSqrdErrors(bool value) { m_hasSqrdErrs = value; } /// Returns a read-only (i.e. const) reference to the specified F array const MantidVec &readF(std::size_t const index) const; /// Set the fractional area array for a given index. void setF(const std::size_t index, const MantidVecPtr &F); + /// Multiply the fractional area arrays by a scale factor + void scaleF(const double scale); + /// Returns if the fractional area is non zero + bool nonZeroF() const; protected: /// Protected copy constructor. May be used by childs for cloning. @@ -74,10 +87,13 @@ protected: /// Flag to indicate if finalize has been called, and if errors/variance used bool m_finalized; + /// Flag to indiciate if the finalized data used squared errors + bool m_hasSqrdErrs; + private: RebinnedOutput *doClone() const override { return new RebinnedOutput(*this); } RebinnedOutput *doCloneEmpty() const override { - return new RebinnedOutput(m_finalized); + return new RebinnedOutput(m_finalized, m_hasSqrdErrs); } }; diff --git a/Framework/DataObjects/src/FractionalRebinning.cpp b/Framework/DataObjects/src/FractionalRebinning.cpp index a645ace793659e49f68682240bb89a8dbe6b083c..3064644bf89025447f3f597f81f79af207f2ed01 100644 --- a/Framework/DataObjects/src/FractionalRebinning.cpp +++ b/Framework/DataObjects/src/FractionalRebinning.cpp @@ -663,6 +663,8 @@ void rebinToFractionalOutput(const Quadrilateral &inputQ, if (inputRB->isFinalized()) { signal *= inF[j]; error *= inF[j]; + if (inputRB->hasSqrdErrors()) + error *= inF[j]; } } @@ -683,6 +685,16 @@ void rebinToFractionalOutput(const Quadrilateral &inputQ, } } +/** + * Called at the completion of the fractional rebinning loop + * to the set the finalize flag in the output workspace. + * @param outputWS Reference to the rebinned output workspace + */ +void finalizeFractionalRebin(RebinnedOutput &outputWS) { + // rebinToFractionalOutput() leaves the data in an unfinalized state + outputWS.setFinalized(false); +} + } // namespace FractionalRebinning } // namespace DataObjects diff --git a/Framework/DataObjects/src/RebinnedOutput.cpp b/Framework/DataObjects/src/RebinnedOutput.cpp index 4c88fd691a336a38acd95ccf677e973a9f715fb3..04570f44d9d606767c997e116cc8c408b5ff73aa 100644 --- a/Framework/DataObjects/src/RebinnedOutput.cpp +++ b/Framework/DataObjects/src/RebinnedOutput.cpp @@ -82,7 +82,7 @@ const MantidVec &RebinnedOutput::readF(const std::size_t index) const { } /** - * Function that sets the fractional area arrat for a given index. + * Function that sets the fractional area array for a given index. * @param index :: the particular array to set * @param F :: the array contained the information */ @@ -91,28 +91,73 @@ void RebinnedOutput::setF(const std::size_t index, const MantidVecPtr &F) { } /** - * This function takes the data/error arrays and divides them by the - * corresponding fractional area array. This creates a representation that - * is easily visualized. The Rebin and Integration algorithms will have to - * undo this in order to properly treat the data. - * @param hasSqrdErrs :: does the workspace have squared errors? - * @param force :: ignore finalize flag or not? + * Function that scales the fractional area arrays. + * @param scale :: the scale factor */ -void RebinnedOutput::finalize(bool hasSqrdErrs, bool force) { - if (m_finalized && !force) - return; - auto nHist = static_cast<int>(this->getNumberHistograms()); +void RebinnedOutput::scaleF(const double scale) { + std::size_t nHist = this->getNumberHistograms(); + for (std::size_t i = 0; i < nHist; ++i) { + MantidVec &frac = this->dataF(i); + std::transform(frac.begin(), frac.end(), frac.begin(), + [scale](double x) { return scale * x; }); + } +} + +/** + * The functions checks if the fractional area data is non zero. + */ +bool RebinnedOutput::nonZeroF() const { + // Checks that the fractions are not all zeros. + auto nHist = static_cast<int>(this->getNumberHistograms()); bool frac_all_zeros = true; for (int i = 0; i < nHist; ++i) { - MantidVec &frac = this->dataF(i); + const MantidVec &frac = this->readF(i); if (std::accumulate(frac.begin(), frac.end(), 0.) != 0) { frac_all_zeros = false; break; } } - if (frac_all_zeros) + return !frac_all_zeros; +} + +/** + * This function takes the data/error arrays and divides them by the + * corresponding fractional area array. This creates a representation that + * is easily visualized. The Rebin and Integration algorithms will have to + * undo this in order to properly treat the data. If the fractional area + * is all zero skip the operation as there will be no meaningful data. + * @param hasSqrdErrs :: does the workspace have squared errors? + */ +void RebinnedOutput::finalize(bool hasSqrdErrs) { + if (m_finalized && hasSqrdErrs == m_hasSqrdErrs) return; + + // Checks that the fractions are not all zeros. + if (!this->nonZeroF()) + return; + + // Fix the squared error representation before returning + auto nHist = static_cast<int>(this->getNumberHistograms()); + if (m_finalized) { + PARALLEL_FOR_IF(Kernel::threadSafe(*this)) + for (int i = 0; i < nHist; ++i) { + MantidVec &err = this->dataE(i); + MantidVec &frac = this->dataF(i); + if (hasSqrdErrs) { + std::transform(err.begin(), err.end(), frac.begin(), err.begin(), + std::divides<double>()); + } else { + std::transform(err.begin(), err.end(), frac.begin(), err.begin(), + std::multiplies<double>()); + } + } + + // Sets flag so subsequent algorithms know to correctly treat data + m_hasSqrdErrs = hasSqrdErrs; + return; + } + PARALLEL_FOR_IF(Kernel::threadSafe(*this)) for (int i = 0; i < nHist; ++i) { MantidVec &data = this->dataY(i); @@ -127,19 +172,21 @@ void RebinnedOutput::finalize(bool hasSqrdErrs, bool force) { std::divides<double>()); } } - // Sets flag so subsequent algorithms know to correctly treat data + // Sets flags so subsequent algorithms know to correctly treat data m_finalized = true; + m_hasSqrdErrs = hasSqrdErrs; } /** * This function "unfinalizes" the workspace by taking the data/error arrays - * and multiplying them by the corresponding fractional area array. - * @param hasSqrdErrs :: does the workspace have squared errors? - * @param force :: ignore finalize flag or not? + * and multiplying them by the corresponding fractional area array if the array + * is non zero. If all the values are zero skip the process as all data will + * will be lost. */ -void RebinnedOutput::unfinalize(bool hasSqrdErrs, bool force) { - if (!m_finalized && !force) +void RebinnedOutput::unfinalize() { + if (!m_finalized || !this->nonZeroF()) return; + auto nHist = static_cast<int>(this->getNumberHistograms()); PARALLEL_FOR_IF(Kernel::threadSafe(*this)) for (int i = 0; i < nHist; ++i) { @@ -150,7 +197,7 @@ void RebinnedOutput::unfinalize(bool hasSqrdErrs, bool force) { std::multiplies<double>()); std::transform(err.begin(), err.end(), frac.begin(), err.begin(), std::multiplies<double>()); - if (hasSqrdErrs) { + if (this->m_hasSqrdErrs) { std::transform(err.begin(), err.end(), frac.begin(), err.begin(), std::multiplies<double>()); } diff --git a/Framework/DataObjects/src/ReflectometryTransform.cpp b/Framework/DataObjects/src/ReflectometryTransform.cpp index a3231deedb7a1e9f798e555afd6e4eed80886e51..b0a1dd48628308d68db34cff3a4baa6784e52825 100644 --- a/Framework/DataObjects/src/ReflectometryTransform.cpp +++ b/Framework/DataObjects/src/ReflectometryTransform.cpp @@ -513,6 +513,7 @@ MatrixWorkspace_sptr ReflectometryTransform::executeNormPoly( } } } + FractionalRebinning::finalizeFractionalRebin(*outWS); outWS->finalize(); FractionalRebinning::normaliseOutput(outWS, inputWS); // Set the output spectrum-detector mapping diff --git a/Framework/Nexus/src/NexusFileIO.cpp b/Framework/Nexus/src/NexusFileIO.cpp index 33c950ebee21fafba10cf687b1a7e41f2e13e3f1..acad6618507121d287b9ac711c689d78e8e1677e 100644 --- a/Framework/Nexus/src/NexusFileIO.cpp +++ b/Framework/Nexus/src/NexusFileIO.cpp @@ -404,6 +404,12 @@ int NexusFileIO::writeNexusProcessedData2D( NXputslab(fileID, rebin_workspace->readF(s).data(), start, asize); start[0]++; } + + std::string finalized = (rebin_workspace->isFinalized()) ? "1" : "0"; + NXputattr(fileID, "finalized", finalized.c_str(), 2, NX_CHAR); + std::string sqrdErrs = (rebin_workspace->hasSqrdErrors()) ? "1" : "0"; + NXputattr(fileID, "sqrd_errors", sqrdErrs.c_str(), 2, NX_CHAR); + if (m_progress != nullptr) m_progress->reportIncrement(1, "Writing data"); } diff --git a/Framework/PythonInterface/mantid/dataobjects/src/Exports/RebinnedOutput.cpp b/Framework/PythonInterface/mantid/dataobjects/src/Exports/RebinnedOutput.cpp index 422c9239cccf0572896e998ddf1fc5a034866ac1..c241b305e68462d8e917d079fd32eee78b3d86d6 100644 --- a/Framework/PythonInterface/mantid/dataobjects/src/Exports/RebinnedOutput.cpp +++ b/Framework/PythonInterface/mantid/dataobjects/src/Exports/RebinnedOutput.cpp @@ -5,22 +5,92 @@ // & Institut Laue - Langevin // SPDX - License - Identifier: GPL - 3.0 + #include "MantidDataObjects/RebinnedOutput.h" +#include "MantidPythonInterface/core/Converters/NDArrayToVector.h" +#include "MantidPythonInterface/core/Converters/PySequenceToVector.h" +#include "MantidPythonInterface/core/Converters/WrapWithNDArray.h" #include "MantidPythonInterface/core/GetPointer.h" +#include "MantidPythonInterface/core/Policies/VectorToNumpy.h" #include "MantidPythonInterface/kernel/Registry/RegisterWorkspacePtrToPython.h" #include <boost/python/class.hpp> using Mantid::DataObjects::RebinnedOutput; using Mantid::DataObjects::Workspace2D; +using namespace Mantid::PythonInterface; +using namespace Mantid::PythonInterface::Converters; +using namespace Mantid::PythonInterface::Policies; using namespace Mantid::PythonInterface::Registry; using namespace boost::python; GET_POINTER_SPECIALIZATION(RebinnedOutput) +namespace { +/// Typedef for data access, i.e. dataX,Y,E members +using data_modifier = Mantid::MantidVec &(RebinnedOutput::*)(const std::size_t); + +/// return_value_policy for read-only numpy array +using return_readonly_numpy = + return_value_policy<VectorRefToNumpy<WrapReadOnly>>; +/// return_value_policy for read-write numpy array +using return_readwrite_numpy = + return_value_policy<VectorRefToNumpy<WrapReadWrite>>; + +/** + * Set the F values from an python array-style object + * @param self :: A reference to the calling object + * @param wsIndex :: The workspace index for the spectrum to set + * @param values :: A numpy array, length must match F array length + */ +void setFFromPyObject(RebinnedOutput &self, const size_t wsIndex, + const boost::python::object &values) { + + if (NDArray::check(values)) { + NDArrayToVector<double> converter(values); + converter.copyTo(self.dataF(wsIndex)); + } else { + PySequenceToVector<double> converter(values); + converter.copyTo(self.dataF(wsIndex)); + } +} +} // namespace + void export_RebinnedOutput() { class_<RebinnedOutput, bases<Workspace2D>, boost::noncopyable>( - "RebinnedOutput", no_init); - + "RebinnedOutput", no_init) + .def("readF", &RebinnedOutput::readF, + (arg("self"), arg("workspaceIndex")), return_readonly_numpy(), + "Creates a read-only numpy wrapper " + "around the original F data at the " + "given index") + .def("dataF", (data_modifier)&RebinnedOutput::dataF, + return_readwrite_numpy(), args("self", "workspaceIndex"), + "Creates a writable numpy wrapper around the original F data at the " + "given index") + .def("setF", &setFFromPyObject, args("self", "workspaceIndex", "x"), + "Set F values from a python list or numpy array. It performs a " + "simple copy into the array") + .def("scaleF", &RebinnedOutput::scaleF, (arg("self"), arg("scale")), + "Scales the fractional area arrays") + .def("nonZeroF", &RebinnedOutput::nonZeroF, (arg("self")), + "Returns if the fractional area is non zero") + .def("finalize", &RebinnedOutput::finalize, + (arg("self"), arg("hasSqrdErrs")), + "Divides the data/error arrays by the corresponding fractional area " + "array") + .def("unfinalize", &RebinnedOutput::unfinalize, (arg("self")), + "Multiplies the data/error arrays by the corresponding fractional " + "area array") + .def("isFinalized", &RebinnedOutput::isFinalized, (arg("self")), + "Returns if values are normalized to the fractional area array") + .def("hasSqrdErrors", &RebinnedOutput::hasSqrdErrors, (arg("self")), + "Returns if squared errors are used with fractional area " + "normalization") + .def("setFinalized", &RebinnedOutput::setFinalized, + (arg("self"), arg("value")), + "Sets the value of the is finalized flag") + .def("setSqrdErrors", &RebinnedOutput::setSqrdErrors, + (arg("self"), arg("value")), + "Sets the value of the squared errors flag"); // register pointers RegisterWorkspacePtrToPython<RebinnedOutput>(); } diff --git a/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py b/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py index 5bed2dd3f6475cb1bc735b5915c70af91c728edc..d334cdb21a2dc9d9ea8223a9914fd84ede773205 100644 --- a/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py +++ b/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py @@ -458,6 +458,53 @@ class MatrixWorkspaceTest(unittest.TestCase): for index in maskedBinsIndices: self.assertEqual(1, index) + def test_rebinnedOutput(self): + rebin = WorkspaceFactory.create("RebinnedOutput", 2, 3, 2) + self.assertFalse(rebin.nonZeroF()) + fv = rebin.readF(1) + rebin.dataY(1)[:] = 10.0 + rebin.dataE(1)[:] = 1.0 + twos = np.ones(len(fv)) * 2.0 + rebin.setF(1, twos) + self.assertTrue(rebin.nonZeroF()) + rebin.setFinalized(False) + rebin.setSqrdErrors(False) + rebin.unfinalize() + self.assertFalse(rebin.isFinalized()) + yv = rebin.readY(1) + ev = rebin.readE(1) + self.assertAlmostEqual(yv[0], 10.0) + self.assertAlmostEqual(ev[0], 1.0) + + rebin.finalize(True) + self.assertTrue(rebin.isFinalized()) + self.assertTrue(rebin.hasSqrdErrors()) + yv = rebin.readY(1) + ev = rebin.readE(1) + self.assertAlmostEqual(yv[0], 5.0) + self.assertAlmostEqual(ev[0], 0.25) + + rebin.finalize(False) + self.assertTrue(rebin.isFinalized()) + self.assertFalse(rebin.hasSqrdErrors()) + yv = rebin.readY(1) + ev = rebin.readE(1) + self.assertAlmostEqual(yv[0], 5.0) + self.assertAlmostEqual(ev[0], 0.5) + + rebin.unfinalize() + self.assertFalse(rebin.isFinalized()) + yv = rebin.readY(1) + ev = rebin.readE(1) + self.assertAlmostEqual(yv[0], 10.0) + self.assertAlmostEqual(ev[0], 1.0) + + rebin.scaleF(2.0) + fv = rebin.readF(1) + self.assertAlmostEqual(fv[0], 4.0) + + + if __name__ == '__main__': unittest.main() diff --git a/Framework/PythonInterface/test/python/mantid/api/WorkspaceFactoryTest.py b/Framework/PythonInterface/test/python/mantid/api/WorkspaceFactoryTest.py index 1a8c96c7f2c2a8006f55b59ef75cea703e098e51..9aae031906ca55eab92f740ad840508a7f8b1883 100644 --- a/Framework/PythonInterface/test/python/mantid/api/WorkspaceFactoryTest.py +++ b/Framework/PythonInterface/test/python/mantid/api/WorkspaceFactoryTest.py @@ -20,9 +20,9 @@ class WorkspaceFactoryTest(unittest.TestCase): return WorkspaceFactory.create("Workspace2D", NVectors=nhist, XLength=xlength, YLength=ylength) - def _verify(self, wksp, nhist, xlength, ylength): + def _verify(self, wksp, nhist, xlength, ylength, wsId="Workspace2D"): self.assertTrue(isinstance(wksp, MatrixWorkspace)) - self.assertEqual(wksp.id(), "Workspace2D") + self.assertEqual(wksp.id(), wsId) self.assertEqual(wksp.getNumberHistograms(), nhist) self.assertEqual(len(wksp.readX(0)), xlength) self.assertEqual(wksp.blocksize(), ylength) @@ -58,5 +58,12 @@ class WorkspaceFactoryTest(unittest.TestCase): peaks = WorkspaceFactory.createPeaks() self.assertTrue(isinstance(peaks, IPeaksWorkspace)) + def test_creating_rebinnedoutput(self): + rebin = WorkspaceFactory.create("RebinnedOutput", 2, 6, 5) + self._verify(rebin, 2, 6, 5, wsId="RebinnedOutput") + fv = rebin.readF(1) + self.assertEqual(len(fv), 5) + + if __name__ == '__main__': unittest.main() diff --git a/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp b/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp index 1214b02df26391c7df8834cafd7a71a82819df1c..04a8c90d5ff1b095d7e438e43efa579cff40b4e5 100644 --- a/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp +++ b/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp @@ -1278,13 +1278,14 @@ RebinnedOutput_sptr createRebinnedOutputWorkspace() { // Set representation outputWS->finalize(); - // Make errors squared rooted + // Make errors squared rooted and set sqrd err flag for (int i = 0; i < numHist; ++i) { auto &mutableE = outputWS->mutableE(i); for (int j = 0; j < numX - 1; ++j) { mutableE[j] = std::sqrt(mutableE[j]); } } + outputWS->setSqrdErrors(false); return outputWS; }