diff --git a/Framework/Algorithms/CMakeLists.txt b/Framework/Algorithms/CMakeLists.txt index 4780746b5984885c3351dca413f4a12703397529..686aa3012da1c3d0bc822767c5c7308f889a27a0 100644 --- a/Framework/Algorithms/CMakeLists.txt +++ b/Framework/Algorithms/CMakeLists.txt @@ -221,7 +221,8 @@ set ( SRC_FILES src/Plus.cpp src/PointByPointVCorrection.cpp src/PoissonErrors.cpp - src/PolarizationCorrection.cpp + src/PolarizationCorrectionFredrikze.cpp + src/PolarizationCorrectionWildes.cpp src/PolarizationEfficiencyCor.cpp src/PolynomialCorrection.cpp src/Power.cpp @@ -559,7 +560,8 @@ set ( INC_FILES inc/MantidAlgorithms/Plus.h inc/MantidAlgorithms/PointByPointVCorrection.h inc/MantidAlgorithms/PoissonErrors.h - inc/MantidAlgorithms/PolarizationCorrection.h + inc/MantidAlgorithms/PolarizationCorrectionFredrikze.h + inc/MantidAlgorithms/PolarizationCorrectionWildes.h inc/MantidAlgorithms/PolarizationEfficiencyCor.h inc/MantidAlgorithms/PolynomialCorrection.h inc/MantidAlgorithms/Power.h @@ -899,7 +901,8 @@ set ( TEST_FILES PlusTest.h PointByPointVCorrectionTest.h PoissonErrorsTest.h - PolarizationCorrectionTest.h + PolarizationCorrectionFredrikzeTest.h + PolarizationCorrectionWildesTest.h PolarizationEfficiencyCorTest.h PolynomialCorrectionTest.h PowerLawCorrectionTest.h diff --git a/Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrection.h b/Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrectionFredrikze.h similarity index 75% rename from Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrection.h rename to Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrectionFredrikze.h index 07a29b9ac673c631055fc1bef90e9b75fe0a5c06..05b1066acaca172064a381fbc168b17029eee58d 100644 --- a/Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrection.h +++ b/Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrectionFredrikze.h @@ -1,5 +1,5 @@ -#ifndef MANTID_ALGORITHMS_POLARIZATIONCORRECTION_H_ -#define MANTID_ALGORITHMS_POLARIZATIONCORRECTION_H_ +#ifndef MANTID_ALGORITHMS_POLARIZATIONCORRECTIONFREDRIKZE_H_ +#define MANTID_ALGORITHMS_POLARIZATIONCORRECTIONFREDRIKZE_H_ #include "MantidKernel/System.h" #include "MantidAPI/Algorithm.h" @@ -13,8 +13,11 @@ class MatrixWorkspace; } namespace Algorithms { -/** PolarizationCorrection : Algorithm to perform polarisation corrections on - multi-period group workspaces. +/** PolarizationCorrectionFredrikze : Algorithm to perform polarisation + corrections on + multi-period group workspaces that implements the Fredrikze (Dutch) method. + Fredrikze, H, et al. “Calibration of a polarized neutron reflectometer” Physica + B 297 (2001) Copyright © 2014 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source @@ -37,7 +40,7 @@ namespace Algorithms { File change history is stored at: <https://github.com/mantidproject/mantid> Code Documentation is available at: <http://doxygen.mantidproject.org> */ -class DLLExport PolarizationCorrection : public API::Algorithm { +class DLLExport PolarizationCorrectionFredrikze : public API::Algorithm { public: const std::string name() const override; int version() const override; @@ -50,9 +53,8 @@ public: private: void init() override; void exec() override; - boost::shared_ptr<Mantid::API::MatrixWorkspace> execPolynomialCorrection( - boost::shared_ptr<Mantid::API::MatrixWorkspace> &input, - const std::vector<double> &coefficients); + boost::shared_ptr<Mantid::API::MatrixWorkspace> + getEfficiencyWorkspace(const std::string &label); boost::shared_ptr<Mantid::API::WorkspaceGroup> execPA(boost::shared_ptr<Mantid::API::WorkspaceGroup> inWS); boost::shared_ptr<Mantid::API::WorkspaceGroup> @@ -63,13 +65,9 @@ private: boost::shared_ptr<Mantid::API::MatrixWorkspace> multiply(boost::shared_ptr<Mantid::API::MatrixWorkspace> &lhsWS, const double &rhs); - boost::shared_ptr<Mantid::API::MatrixWorkspace> - copyShapeAndFill(boost::shared_ptr<Mantid::API::MatrixWorkspace> &base, - const double &value); - bool isPropertyDefault(const std::string &propertyName) const; }; } // namespace Algorithms } // namespace Mantid -#endif /* MANTID_ALGORITHMS_POLARIZATIONCORRECTION_H_ */ +#endif /* MANTID_ALGORITHMS_POLARIZATIONCORRECTIONFREDRIKZE_H_ */ diff --git a/Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrectionWildes.h b/Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrectionWildes.h new file mode 100644 index 0000000000000000000000000000000000000000..08b67c08cb8c9a41c7cbaa92164e3b9a4a75acc5 --- /dev/null +++ b/Framework/Algorithms/inc/MantidAlgorithms/PolarizationCorrectionWildes.h @@ -0,0 +1,98 @@ +#ifndef MANTID_ALGORITHMS_POLARIZATIONCORRECTIONWILDES_H_ +#define MANTID_ALGORITHMS_POLARIZATIONCORRECTIONWILDES_H_ + +#include "MantidAlgorithms/DllConfig.h" +#include "MantidAPI/Algorithm.h" +#include "MantidAPI/WorkspaceGroup_fwd.h" + +namespace Mantid { +namespace API { +class ISpectrum; +} + +namespace Algorithms { + +/** PolarizationCorrectionWildes : This algorithm corrects for non-ideal + component efficiencies in polarized neutron analysis. It is based on + [A. R. Wildes (2006) Neutron News, 17:2, 17-25, + DOI: 10.1080/10448630600668738] + + Copyright © 2018 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> +*/ +class MANTID_ALGORITHMS_DLL PolarizationCorrectionWildes + : 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: + /// A convenience set of workspaces corresponding flipper configurations. + struct WorkspaceMap { + API::MatrixWorkspace_sptr mmWS{nullptr}; + API::MatrixWorkspace_sptr mpWS{nullptr}; + API::MatrixWorkspace_sptr pmWS{nullptr}; + API::MatrixWorkspace_sptr ppWS{nullptr}; + size_t size() const noexcept; + }; + + /// A convenience set of efficiency factors. + struct EfficiencyMap { + const API::ISpectrum *P1{nullptr}; + const API::ISpectrum *P2{nullptr}; + const API::ISpectrum *F1{nullptr}; + const API::ISpectrum *F2{nullptr}; + }; + + void init() override; + void exec() override; + std::map<std::string, std::string> validateInputs() override; + void checkConsistentNumberHistograms(const WorkspaceMap &inputs); + void checkConsistentX(const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + EfficiencyMap efficiencyFactors(); + WorkspaceMap directBeamCorrections(const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + WorkspaceMap analyzerlessCorrections(const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + WorkspaceMap twoInputCorrections(const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + WorkspaceMap threeInputCorrections(const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + WorkspaceMap fullCorrections(const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + API::WorkspaceGroup_sptr groupOutput(const WorkspaceMap &outputs); + WorkspaceMap mapInputsToDirections(const std::vector<std::string> &flippers); + void threeInputsSolve01(WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + void threeInputsSolve10(WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); + void twoInputsSolve01And10(WorkspaceMap &fullInputs, + const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies); +}; + +} // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_POLARIZATIONCORRECTIONWILDES_H_ */ diff --git a/Framework/Algorithms/inc/MantidAlgorithms/PolarizationEfficiencyCor.h b/Framework/Algorithms/inc/MantidAlgorithms/PolarizationEfficiencyCor.h index a84f32e031f8438b57c56ca2048771cc9befbc91..11f12d56958e2fa63dfb4b6af188d79acf7acbcc 100644 --- a/Framework/Algorithms/inc/MantidAlgorithms/PolarizationEfficiencyCor.h +++ b/Framework/Algorithms/inc/MantidAlgorithms/PolarizationEfficiencyCor.h @@ -6,16 +6,12 @@ #include "MantidAPI/WorkspaceGroup_fwd.h" namespace Mantid { -namespace API { -class ISpectrum; -} - namespace Algorithms { -/** PolarizationEfficiencyCor : This algorithm corrects for non-ideal - component efficiencies in polarized neutron analysis. It is based on - [A. R. Wildes (2006) Neutron News, 17:2, 17-25, - DOI: 10.1080/10448630600668738] +/** PolarizationEfficiencyCor: a generalised polarization correction + algorithm. Depending on the value of property "CorrectionMethod" it + calls either PolarizationCorrectionFredrikze or PolarizationCorrectionWildes + inetrnally. Copyright © 2018 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source @@ -49,49 +45,24 @@ public: const std::string summary() const override; private: - /// A convenience set of workspaces corresponding flipper configurations. - struct WorkspaceMap { - API::MatrixWorkspace_sptr mmWS{nullptr}; - API::MatrixWorkspace_sptr mpWS{nullptr}; - API::MatrixWorkspace_sptr pmWS{nullptr}; - API::MatrixWorkspace_sptr ppWS{nullptr}; - size_t size() const noexcept; - }; - - /// A convenience set of efficiency factors. - struct EfficiencyMap { - const API::ISpectrum *P1{nullptr}; - const API::ISpectrum *P2{nullptr}; - const API::ISpectrum *F1{nullptr}; - const API::ISpectrum *F2{nullptr}; - }; - void init() override; void exec() override; - std::map<std::string, std::string> validateInputs() override; - void checkConsistentNumberHistograms(const WorkspaceMap &inputs); - void checkConsistentX(const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - EfficiencyMap efficiencyFactors(); - WorkspaceMap directBeamCorrections(const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - WorkspaceMap analyzerlessCorrections(const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - WorkspaceMap twoInputCorrections(const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - WorkspaceMap threeInputCorrections(const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - WorkspaceMap fullCorrections(const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - API::WorkspaceGroup_sptr groupOutput(const WorkspaceMap &outputs); - WorkspaceMap mapInputsToDirections(const std::vector<std::string> &flippers); - void threeInputsSolve01(WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - void threeInputsSolve10(WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); - void twoInputsSolve01And10(WorkspaceMap &fullInputs, - const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies); + void execWildes(); + void execFredrikze(); + + void checkWorkspaces() const; + void checkWildesProperties() const; + void checkFredrikzeProperties() const; + + std::vector<std::string> getWorkspaceNameList() const; + API::WorkspaceGroup_sptr getWorkspaceGroup() const; + API::MatrixWorkspace_sptr getEfficiencies(); + bool needInterpolation(API::MatrixWorkspace const &efficiencies, + API::MatrixWorkspace const &inWS) const; + API::MatrixWorkspace_sptr + convertToHistogram(API::MatrixWorkspace_sptr efficiencies); + API::MatrixWorkspace_sptr interpolate(API::MatrixWorkspace_sptr efficiencies, + API::MatrixWorkspace_sptr inWS); }; } // namespace Algorithms diff --git a/Framework/Algorithms/src/PolarizationCorrection.cpp b/Framework/Algorithms/src/PolarizationCorrectionFredrikze.cpp similarity index 59% rename from Framework/Algorithms/src/PolarizationCorrection.cpp rename to Framework/Algorithms/src/PolarizationCorrectionFredrikze.cpp index b3c949ab0997a07d1c6a669a21fae007e0109d2c..ccc49951484ab2c478347ec7601c21c1f3ca7b4f 100644 --- a/Framework/Algorithms/src/PolarizationCorrection.cpp +++ b/Framework/Algorithms/src/PolarizationCorrectionFredrikze.cpp @@ -1,13 +1,13 @@ -#include "MantidAlgorithms/PolarizationCorrection.h" +#include "MantidAlgorithms/PolarizationCorrectionFredrikze.h" #include "MantidAPI/Axis.h" +#include "MantidAPI/TextAxis.h" +#include "MantidAPI/MatrixWorkspace.h" #include "MantidAPI/WorkspaceFactory.h" #include "MantidAPI/WorkspaceGroup.h" #include "MantidAPI/WorkspaceHistory.h" #include "MantidDataObjects/WorkspaceSingleValue.h" -#include "MantidKernel/ArrayProperty.h" -#include "MantidKernel/ListValidator.h" -#include "MantidKernel/Unit.h" #include "MantidGeometry/Instrument.h" +#include "MantidKernel/ListValidator.h" #include <boost/shared_ptr.hpp> @@ -19,22 +19,24 @@ using namespace Mantid::Geometry; namespace { -const std::string pNRLabel() { return "PNR"; } +const std::string pNRLabel("PNR"); + +const std::string pALabel("PA"); -const std::string pALabel() { return "PA"; } +const std::string crhoLabel("Rho"); -const std::string crhoLabel() { return "CRho"; } +const std::string cppLabel("Pp"); -const std::string cppLabel() { return "CPp"; } +const std::string cAlphaLabel("Alpha"); -const std::string cAlphaLabel() { return "CAlpha"; } +const std::string cApLabel("Ap"); -const std::string cApLabel() { return "CAp"; } +const std::string efficienciesLabel("Efficiencies"); std::vector<std::string> modes() { std::vector<std::string> modes; - modes.push_back(pALabel()); - modes.push_back(pNRLabel()); + modes.push_back(pALabel); + modes.push_back(pNRLabel); return modes; } @@ -103,38 +105,32 @@ void validateInputWorkspace(WorkspaceGroup_sptr &ws) { } using VecDouble = std::vector<double>; -} +} // namespace namespace Mantid { namespace Algorithms { // Register the algorithm into the AlgorithmFactory -DECLARE_ALGORITHM(PolarizationCorrection) +DECLARE_ALGORITHM(PolarizationCorrectionFredrikze) //---------------------------------------------------------------------------------------------- /// Algorithm's name for identification. @see Algorithm::name -const std::string PolarizationCorrection::name() const { - return "PolarizationCorrection"; +const std::string PolarizationCorrectionFredrikze::name() const { + return "PolarizationCorrectionFredrikze"; } /// Algorithm's version for identification. @see Algorithm::version -int PolarizationCorrection::version() const { return 1; } +int PolarizationCorrectionFredrikze::version() const { return 1; } /// Algorithm's category for identification. @see Algorithm::category -const std::string PolarizationCorrection::category() const { +const std::string PolarizationCorrectionFredrikze::category() const { return "Reflectometry"; } -bool PolarizationCorrection::isPropertyDefault( - const std::string &propertyName) const { - Property *prop = this->getProperty(propertyName); - return prop->isDefault(); -} - /** * @return Return the algorithm summary. */ -const std::string PolarizationCorrection::summary() const { +const std::string PolarizationCorrectionFredrikze::summary() const { return "Makes corrections for polarization efficiencies of the polarizer and " "analyzer in a reflectometry neutron spectrometer."; } @@ -146,8 +142,8 @@ const std::string PolarizationCorrection::summary() const { * @return Multiplied Workspace. */ MatrixWorkspace_sptr -PolarizationCorrection::multiply(MatrixWorkspace_sptr &lhsWS, - const double &rhs) { +PolarizationCorrectionFredrikze::multiply(MatrixWorkspace_sptr &lhsWS, + const double &rhs) { auto multiply = this->createChildAlgorithm("Multiply"); auto rhsWS = boost::make_shared<DataObjects::WorkspaceSingleValue>(rhs); multiply->initialize(); @@ -164,8 +160,9 @@ PolarizationCorrection::multiply(MatrixWorkspace_sptr &lhsWS, * @param rhs Value to add * @return Summed workspace */ -MatrixWorkspace_sptr PolarizationCorrection::add(MatrixWorkspace_sptr &lhsWS, - const double &rhs) { +MatrixWorkspace_sptr +PolarizationCorrectionFredrikze::add(MatrixWorkspace_sptr &lhsWS, + const double &rhs) { auto plus = this->createChildAlgorithm("Plus"); auto rhsWS = boost::make_shared<DataObjects::WorkspaceSingleValue>(rhs); plus->initialize(); @@ -179,7 +176,7 @@ MatrixWorkspace_sptr PolarizationCorrection::add(MatrixWorkspace_sptr &lhsWS, //---------------------------------------------------------------------------------------------- /** Initialize the algorithm's properties. */ -void PolarizationCorrection::init() { +void PolarizationCorrectionFredrikze::init() { declareProperty(make_unique<WorkspaceProperty<Mantid::API::WorkspaceGroup>>( "InputWorkspace", "", Direction::Input), "An input workspace to process."); @@ -192,67 +189,18 @@ void PolarizationCorrection::init() { "PA: Full Polarization Analysis PNR-PA"); declareProperty( - Kernel::make_unique<ArrayProperty<double>>(cppLabel(), Direction::Input), - "Effective polarizing power of the polarizing system. " - "Expressed as a ratio 0 < Pp < 1"); - - declareProperty( - Kernel::make_unique<ArrayProperty<double>>(cApLabel(), Direction::Input), - "Effective polarizing power of the analyzing system. " - "Expressed as a ratio 0 < Ap < 1"); - - declareProperty( - Kernel::make_unique<ArrayProperty<double>>(crhoLabel(), Direction::Input), - "Ratio of efficiencies of polarizer spin-down to polarizer " - "spin-up. This is characteristic of the polarizer flipper. " - "Values are constants for each term in a polynomial " - "expression."); - - declareProperty(Kernel::make_unique<ArrayProperty<double>>(cAlphaLabel(), - Direction::Input), - "Ratio of efficiencies of analyzer spin-down to analyzer " - "spin-up. This is characteristic of the analyzer flipper. " - "Values are factors for each term in a polynomial " - "expression."); + Kernel::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>( + efficienciesLabel, "", Kernel::Direction::Input), + "A workspace containing the efficiency factors Pp, Ap, Rho and Alpha " + "as histograms"); declareProperty(make_unique<WorkspaceProperty<Mantid::API::WorkspaceGroup>>( "OutputWorkspace", "", Direction::Output), "An output workspace."); } -MatrixWorkspace_sptr PolarizationCorrection::execPolynomialCorrection( - MatrixWorkspace_sptr &input, const VecDouble &coefficients) { - auto polyCorr = this->createChildAlgorithm("PolynomialCorrection"); - polyCorr->initialize(); - polyCorr->setProperty("InputWorkspace", input); - polyCorr->setProperty("Coefficients", coefficients); - polyCorr->execute(); - MatrixWorkspace_sptr corrected = polyCorr->getProperty("OutputWorkspace"); - return corrected; -} - -MatrixWorkspace_sptr -PolarizationCorrection::copyShapeAndFill(MatrixWorkspace_sptr &base, - const double &value) { - MatrixWorkspace_sptr wsTemplate = WorkspaceFactory::Instance().create(base); - // Copy the x-array across to the new workspace. - for (size_t i = 0; i < wsTemplate->getNumberHistograms(); ++i) { - wsTemplate->setSharedX(i, base->sharedX(i)); - } - auto zeroed = this->multiply(wsTemplate, 0); - auto filled = this->add(zeroed, value); - return filled; -} - -WorkspaceGroup_sptr PolarizationCorrection::execPA(WorkspaceGroup_sptr inWS) { - - if (isPropertyDefault(cAlphaLabel())) { - throw std::invalid_argument("Must provide as input for PA: " + - cAlphaLabel()); - } - if (isPropertyDefault(cApLabel())) { - throw std::invalid_argument("Must provide as input for PA: " + cApLabel()); - } +WorkspaceGroup_sptr +PolarizationCorrectionFredrikze::execPA(WorkspaceGroup_sptr inWS) { size_t itemIndex = 0; MatrixWorkspace_sptr Ipp = @@ -269,30 +217,10 @@ WorkspaceGroup_sptr PolarizationCorrection::execPA(WorkspaceGroup_sptr inWS) { Ipa->setTitle("Ipa"); Iap->setTitle("Iap"); - auto cropAlg = this->createChildAlgorithm("CropWorkspace"); - cropAlg->initialize(); - cropAlg->setProperty("InputWorkspace", Ipp); - cropAlg->setProperty("EndWorkspaceIndex", 0); - cropAlg->execute(); - MatrixWorkspace_sptr croppedIpp = cropAlg->getProperty("OutputWorkspace"); - - MatrixWorkspace_sptr ones = copyShapeAndFill(croppedIpp, 1.0); - // The ones workspace is now identical to the input workspaces in x, but has 1 - // as y values. It can therefore be used to build real polynomial functions. - - const VecDouble c_rho = getProperty(crhoLabel()); - const VecDouble c_alpha = getProperty(cAlphaLabel()); - const VecDouble c_pp = getProperty(cppLabel()); - const VecDouble c_ap = getProperty(cApLabel()); - - const auto rho = this->execPolynomialCorrection( - ones, c_rho); // Execute polynomial expression - const auto pp = this->execPolynomialCorrection( - ones, c_pp); // Execute polynomial expression - const auto alpha = this->execPolynomialCorrection( - ones, c_alpha); // Execute polynomial expression - const auto ap = this->execPolynomialCorrection( - ones, c_ap); // Execute polynomial expression + const auto rho = this->getEfficiencyWorkspace(crhoLabel); + const auto pp = this->getEfficiencyWorkspace(cppLabel); + const auto alpha = this->getEfficiencyWorkspace(cAlphaLabel); + const auto ap = this->getEfficiencyWorkspace(cApLabel); const auto A0 = (Iaa * pp * ap) + (ap * Ipa * rho * pp) + (ap * Iap * alpha * pp) + (Ipp * ap * alpha * rho * pp); @@ -341,22 +269,16 @@ WorkspaceGroup_sptr PolarizationCorrection::execPA(WorkspaceGroup_sptr inWS) { return dataOut; } -WorkspaceGroup_sptr PolarizationCorrection::execPNR(WorkspaceGroup_sptr inWS) { +WorkspaceGroup_sptr +PolarizationCorrectionFredrikze::execPNR(WorkspaceGroup_sptr inWS) { size_t itemIndex = 0; MatrixWorkspace_sptr Ip = boost::dynamic_pointer_cast<MatrixWorkspace>(inWS->getItem(itemIndex++)); MatrixWorkspace_sptr Ia = boost::dynamic_pointer_cast<MatrixWorkspace>(inWS->getItem(itemIndex++)); - MatrixWorkspace_sptr ones = copyShapeAndFill(Ip, 1.0); - - const VecDouble c_rho = getProperty(crhoLabel()); - const VecDouble c_pp = getProperty(cppLabel()); - - const auto rho = this->execPolynomialCorrection( - ones, c_rho); // Execute polynomial expression - const auto pp = this->execPolynomialCorrection( - ones, c_pp); // Execute polynomial expression + const auto rho = this->getEfficiencyWorkspace(crhoLabel); + const auto pp = this->getEfficiencyWorkspace(cppLabel); const auto D = pp * (rho + 1); @@ -374,55 +296,74 @@ WorkspaceGroup_sptr PolarizationCorrection::execPNR(WorkspaceGroup_sptr inWS) { return dataOut; } +/** Extract a spectrum from the Efficiencies workspace as a 1D workspace. + * @param label :: A label of the spectrum to extract. + * @return :: A workspace with a single spectrum. + */ +boost::shared_ptr<Mantid::API::MatrixWorkspace> +PolarizationCorrectionFredrikze::getEfficiencyWorkspace( + const std::string &label) { + MatrixWorkspace_sptr efficiencies = getProperty(efficienciesLabel); + auto const &axis = dynamic_cast<TextAxis &>(*efficiencies->getAxis(1)); + size_t index = axis.length(); + for (size_t i = 0; i < axis.length(); ++i) { + if (axis.label(i) == label) { + index = i; + break; + } + } + + if (index == axis.length()) { + // Check if we need to fetch polarization parameters from the instrument's + // parameters + static std::map<std::string, std::string> loadableProperties{ + {crhoLabel, "crho"}, + {cppLabel, "cPp"}, + {cApLabel, "cAp"}, + {cAlphaLabel, "calpha"}}; + WorkspaceGroup_sptr inWS = getProperty("InputWorkspace"); + Instrument_const_sptr instrument = fetchInstrument(inWS.get()); + auto vals = instrument->getStringParameter(loadableProperties[label]); + if (vals.empty()) { + throw std::invalid_argument("Efficiencey property not found: " + label); + } + auto extract = createChildAlgorithm("CreatePolarizationEfficiencies"); + extract->initialize(); + extract->setProperty("InputWorkspace", efficiencies); + extract->setProperty(label, vals.front()); + extract->execute(); + MatrixWorkspace_sptr outWS = extract->getProperty("OutputWorkspace"); + return outWS; + } else { + auto extract = createChildAlgorithm("ExtractSingleSpectrum"); + extract->initialize(); + extract->setProperty("InputWorkspace", efficiencies); + extract->setProperty("WorkspaceIndex", static_cast<int>(index)); + extract->execute(); + MatrixWorkspace_sptr outWS = extract->getProperty("OutputWorkspace"); + return outWS; + } +} + //---------------------------------------------------------------------------------------------- /** Execute the algorithm. */ -void PolarizationCorrection::exec() { +void PolarizationCorrectionFredrikze::exec() { WorkspaceGroup_sptr inWS = getProperty("InputWorkspace"); const std::string analysisMode = getProperty("PolarizationAnalysis"); const size_t nWorkspaces = inWS->size(); validateInputWorkspace(inWS); - Instrument_const_sptr instrument = fetchInstrument(inWS.get()); - - // Check if we need to fetch polarization parameters from the instrument's - // parameters - std::map<std::string, std::string> loadableProperties; - loadableProperties[crhoLabel()] = "crho"; - loadableProperties[cppLabel()] = "cPp"; - - // In PA mode, we also require cap and calpha - if (analysisMode == pALabel()) { - loadableProperties[cApLabel()] = "cAp"; - loadableProperties[cAlphaLabel()] = "calpha"; - } - - for (auto &loadableProperty : loadableProperties) { - Property *prop = getProperty(loadableProperty.first); - - if (!prop) - continue; - - if (prop->isDefault()) { - auto vals = instrument->getStringParameter(loadableProperty.second); - if (vals.empty()) - throw std::runtime_error( - "Cannot find value for " + loadableProperty.first + - " in parameter file. Please specify this property manually."); - prop->setValue(vals[0]); - } - } - WorkspaceGroup_sptr outWS; - if (analysisMode == pALabel()) { + if (analysisMode == pALabel) { if (nWorkspaces != 4) { throw std::invalid_argument( "For PA analysis, input group must have 4 periods."); } g_log.notice("PA polarization correction"); outWS = execPA(inWS); - } else if (analysisMode == pNRLabel()) { + } else if (analysisMode == pNRLabel) { if (nWorkspaces != 2) { throw std::invalid_argument( "For PNR analysis, input group must have 2 periods."); diff --git a/Framework/Algorithms/src/PolarizationCorrectionWildes.cpp b/Framework/Algorithms/src/PolarizationCorrectionWildes.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e9fb51d74c413abd4c59216f54da0355ec7f76e --- /dev/null +++ b/Framework/Algorithms/src/PolarizationCorrectionWildes.cpp @@ -0,0 +1,1036 @@ +#include "MantidAlgorithms/PolarizationCorrectionWildes.h" + +#include "MantidAPI/ADSValidator.h" +#include "MantidAPI/Axis.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/ListValidator.h" +#include "MantidKernel/StringTokenizer.h" + +#include <Eigen/Dense> +#include <boost/math/special_functions/pow.hpp> + +namespace { +/// Property names. +namespace Prop { +static const std::string FLIPPERS{"Flippers"}; +static const std::string EFFICIENCIES{"Efficiencies"}; +static const std::string INPUT_WS{"InputWorkspaces"}; +static const std::string OUTPUT_WS{"OutputWorkspace"}; +} // namespace Prop + +/// Flipper configurations. +namespace Flippers { +static const std::string Off{"0"}; +static const std::string OffOff{"00"}; +static const std::string OffOn{"01"}; +static const std::string On{"1"}; +static const std::string OnOff{"10"}; +static const std::string OnOn{"11"}; +} // namespace Flippers + +/** + * Parse a flipper configuration string. + * @param setupString a configuration string + * @return a vector of individual configurations + */ +std::vector<std::string> parseFlipperSetup(const std::string &setupString) { + using Mantid::Kernel::StringTokenizer; + StringTokenizer tokens{setupString, ",", StringTokenizer::TOK_TRIM}; + return std::vector<std::string>{tokens.begin(), tokens.end()}; +} + +/** + * Throw if given ws is nullptr. + * @param ws a workspace to check + * @param tag a flipper configuration for the error message + */ +void checkInputExists(const Mantid::API::MatrixWorkspace_sptr &ws, + const std::string &tag) { + if (!ws) { + throw std::runtime_error("A workspace designated as " + tag + + " is missing in inputs."); + } +} + +/** + * Calculate the corrected intensities and error estimates. + * @param corrected an output vector for R00, R01, R10 and R11 + * @param errors an output vector for the error estimates + * @param ppy intensity I00 + * @param ppyE error of ppy + * @param pmy intensity I01 + * @param pmyE error of pmy + * @param mpy intensity I10 + * @param mpyE error of mpy + * @param mmy intensity I11 + * @param mmyE error of mmy + * @param f1 polarizer efficiency + * @param f1E error of f1 + * @param f2 analyzer efficiency + * @param f2E error of f2 + * @param p1 polarizer flipper efficiency + * @param p1E error of p1 + * @param p2 analyzer flipper efficiency + * @param p2E error of p2 + */ +void fourInputsCorrectedAndErrors( + Eigen::Vector4d &corrected, Eigen::Vector4d &errors, const double ppy, + const double ppyE, const double pmy, const double pmyE, const double mpy, + const double mpyE, const double mmy, const double mmyE, const double f1, + const double f1E, const double f2, const double f2E, const double p1, + const double p1E, const double p2, const double p2E) { + using namespace boost::math; + // Note that f1 and f2 correspond to 1-F1 and 1-F2 in [Wildes, 1999]. + // These are inverted forms of the efficiency matrices. + const auto diag1 = 1. / f1; + const auto off1 = (f1 - 1.) / f1; + Eigen::Matrix4d F1m; + F1m << 1., 0., 0., 0., 0., 1., 0., 0., off1, 0., diag1, 0., 0., off1, 0., + diag1; + const auto diag2 = 1. / f2; + const auto off2 = (f2 - 1.) / f2; + Eigen::Matrix4d F2m; + F2m << 1., 0., 0., 0., off2, diag2, 0., 0., 0., 0., 1., 0., 0., 0., off2, + diag2; + const auto diag3 = (p1 - 1.) / (2. * p1 - 1.); + const auto off3 = p1 / (2. * p1 - 1); + Eigen::Matrix4d P1m; + P1m << diag3, 0, off3, 0, 0, diag3, 0, off3, off3, 0, diag3, 0, 0, off3, 0, + diag3; + const auto diag4 = (p2 - 1.) / (2. * p2 - 1.); + const auto off4 = p2 / (2. * p2 - 1.); + Eigen::Matrix4d P2m; + P2m << diag4, off4, 0., 0., off4, diag4, 0., 0., 0., 0., diag4, off4, 0., 0., + off4, diag4; + const Eigen::Vector4d intensities(ppy, pmy, mpy, mmy); + const auto FProduct = F2m * F1m; + const auto PProduct = P2m * P1m; + const auto PFProduct = PProduct * FProduct; + corrected = PFProduct * intensities; + // The error matrices here are element-wise algebraic derivatives of + // the matrices above, multiplied by the error. + const auto elemE1 = -1. / pow<2>(f1) * f1E; + Eigen::Matrix4d F1Em; + F1Em << 0., 0., 0., 0., 0., 0., 0., 0., -elemE1, 0., elemE1, 0., 0., -elemE1, + 0., elemE1; + const auto elemE2 = -1. / pow<2>(f2) * f2E; + Eigen::Matrix4d F2Em; + F2Em << 0., 0., 0., 0., -elemE2, elemE2, 0., 0., 0., 0., 0., 0., 0., 0., + -elemE2, elemE2; + const auto elemE3 = 1. / pow<2>(2. * p1 - 1.) * p1E; + Eigen::Matrix4d P1Em; + P1Em << elemE3, 0., -elemE3, 0., 0., elemE3, 0., -elemE3, -elemE3, 0., elemE3, + 0., 0., -elemE3, 0., elemE3; + const auto elemE4 = 1. / pow<2>(2. * p2 - 1.) * p2E; + Eigen::Matrix4d P2Em; + P2Em << elemE4, -elemE4, 0., 0., -elemE4, elemE4, 0., 0., 0., 0., elemE4, + -elemE4, 0., 0., -elemE4, elemE4; + const Eigen::Vector4d yErrors(ppyE, pmyE, mpyE, mmyE); + const auto e1 = (P2Em * P1m * FProduct * intensities).array(); + const auto e2 = (P2m * P1Em * FProduct * intensities).array(); + const auto e3 = (PProduct * F2Em * F1m * intensities).array(); + const auto e4 = (PProduct * F2m * F1Em * intensities).array(); + const auto sqPFProduct = (PFProduct.array() * PFProduct.array()).matrix(); + const auto sqErrors = (yErrors.array() * yErrors.array()).matrix(); + const auto e5 = (sqPFProduct * sqErrors).array(); + errors = (e1 * e1 + e2 * e2 + e3 * e3 + e4 * e4 + e5).sqrt(); +} + +/** + * Estimate errors for I01 in the two inputs case. + * @param i00 intensity of 00 flipper configuration + * @param e00 error of i00 + * @param i11 intensity of 11 flipper configuration + * @param e11 error of i11 + * @param p1 polarizer efficiency + * @param p1E error of p1 + * @param p2 analyzer efficiency + * @param p2E error of p2 + * @param f1 polarizer flipper efficiency + * @param f1E error of f1 + * @param f2 analyzer flipper efficiency + * @param f2E error of f2 + * @return the error estimate + */ +double twoInputsErrorEstimate01(const double i00, const double e00, + const double i11, const double e11, + const double p1, const double p1E, + const double p2, const double p2E, + const double f1, const double f1E, + const double f2, const double f2E) { + using namespace boost::math; + // Derivatives of the equation which solves the I01 intensities + // with respect to i00, i11, f1, etc. + const auto pmdi00 = + -((f1 * (-1. + 2. * p1) * + (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + + (-1. + p2) * p2)) / + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))); + const auto pmdi11 = + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2)) / + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); + const auto pmdf1 = + -(((-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)) * + (f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - + f1 * i00 * (-1. + 2. * p1) * + (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + + (-1. + p2) * p2))) / + pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) - + (i00 * (-1. + 2. * p1) * + (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + + (-1. + p2) * p2)) / + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); + const auto pmdf2 = + -(((f1 * (-1. + 2. * p1) * (-1. + p1 + p2) * (-1 + 2 * p2) + + p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2)) * + (f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - + f1 * i00 * (-1. + 2. * p1) * + (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + + (-1. + p2) * p2))) / + pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + + (-f1 * i00 * (-1. + 2. * p1) * + (-pow<2>(1. - 2. * p2) + 2 * f2 * pow<2>(1. - 2. * p2)) + + i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2)) / + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); + const auto pmdp1 = + -(((f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - + f1 * i00 * (-1. + 2. * p1) * + (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + + (-1. + p2) * p2)) * + (f2 * p1 * (1. - 2. * p2) + + f1 * f2 * (-1. + 2. * p1) * (-1. + 2. * p2) + + f2 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + 2. * f1 * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) / + pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + + (f2 * i11 * p1 * (1. - 2. * p2) + + f2 * i11 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - + 2. * f1 * i00 * (-f2 * pow<2>(1. - 2. * p2) + + pow<2>(f2) * pow<2>(1. - 2. * p2) + (-1. + p2) * p2)) / + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); + const auto pmdp2 = + -(((f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - + f1 * i00 * (-1. + 2. * p1) * + (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + + (-1. + p2) * p2)) * + (f2 * (2. - 2. * p1) * p1 + + f1 * (-1. + 2. * p1) * (1. - 2. * p2 + 2. * f2 * (-1. + p1 + p2) + + f2 * (-1. + 2. * p2)))) / + pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + + (f2 * i11 * (2. - 2. * p1) * p1 - + f1 * i00 * (-1. + 2. * p1) * + (-1. + 4. * f2 * (1. - 2. * p2) - 4. * pow<2>(f2) * (1. - 2. * p2) + + 2. * p2)) / + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); + // Estimate the error components using linearized extrapolation, + // sum in squares. + const auto e01_I00 = pow<2>(pmdi00 * e00); + const auto e01_I11 = pow<2>(pmdi11 * e11); + const auto e01_F1 = pow<2>(pmdf1 * f1E); + const auto e01_F2 = pow<2>(pmdf2 * f2E); + const auto e01_P1 = pow<2>(pmdp1 * p1E); + const auto e01_P2 = pow<2>(pmdp2 * p2E); + return std::sqrt(e01_I00 + e01_I11 + e01_F1 + e01_F2 + e01_P1 + e01_P2); +} + +/** + * Estimate errors for I10 in the two inputs case. + * @param i00 intensity of 00 flipper configuration + * @param e00 error of i00 + * @param i11 intensity of 11 flipper configuration + * @param e11 error of i11 + * @param p1 polarizer efficiency + * @param p1E error of p1 + * @param p2 analyzer efficiency + * @param p2E error of p2 + * @param f1 polarizer flipper efficiency + * @param f1E error of f1 + * @param f2 analyzer flipper efficiency + * @param f2E error of f2 + * @return the error estimate + */ +double twoInputsErrorEstimate10(const double i00, const double e00, + const double i11, const double e11, + const double p1, const double p1E, + const double p2, const double p2E, + const double f1, const double f1E, + const double f2, const double f2E) { + using namespace boost::math; + // Derivatives of the equation which solves the I10 intensities + // with respect to i00, i11, f1, etc. + const auto a = -1. + p1 + 2. * p2 - 2. * p1 * p2; + const auto b = -1. + 2. * p1; + const auto c = -1. + 2. * p2; + const auto d = -1. + p2; + const auto mpdi00 = (-pow<2>(f1) * f2 * pow<2>(b) * c + + f1 * f2 * pow<2>(b) * c + f2 * p1 * a) / + (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); + const auto mpdi11 = -((f1 * b * d * p2) / + (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))); + const auto mpdf1 = + -(((-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)) * + (-pow<2>(f1) * f2 * i00 * pow<2>(1. - 2. * p1) * (-1. + 2. * p2) + + f2 * i00 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + (-i11 * (-1. + p2) * p2 + + f2 * i00 * (-1. + 2. * p1) * (-1. + 2. * p2)))) / + pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + + (-2. * f1 * f2 * i00 * pow<2>(1. - 2. * p1) * (-1. + 2. * p2) + + (-1. + 2. * p1) * (-i11 * (-1. + p2) * p2 + + f2 * i00 * (-1. + 2. * p1) * (-1. + 2. * p2))) / + (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + + f1 * (-1. + 2. * p1) * + ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); + const auto mpdf2 = + -(((f1 * b * (p1 + d) * c + p1 * a) * + (-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + + f1 * b * (-i11 * d * p2 + f2 * i00 * b * c))) / + pow<2>(f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))) + + (-pow<2>(f1) * i00 * pow<2>(b) * c + f1 * i00 * pow<2>(b) * c + + i00 * p1 * a) / + (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); + const auto mpdp1 = + -(((-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + + f1 * b * (-i11 * d * p2 + f2 * i00 * b * c)) * + (f2 * p1 * -c + f1 * f2 * b * c + f2 * a + + 2. * f1 * (-d * p2 + f2 * (p1 + d) * c))) / + pow<2>(f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))) + + (f2 * i00 * p1 * -c + 4. * pow<2>(f1) * f2 * i00 * -b * c + + 2. * f1 * f2 * i00 * b * c + f2 * i00 * a + + 2. * f1 * (-i11 * d * p2 + f2 * i00 * b * c)) / + (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); + const auto mpdp2 = + -(((f2 * (2. - 2. * p1) * p1 + + f1 * b * (1. - 2. * p2 + 2. * f2 * (p1 + d) + f2 * c)) * + (-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + + f1 * b * (-i11 * d * p2 + f2 * i00 * b * c))) / + pow<2>(f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))) + + (-2. * pow<2>(f1) * f2 * i00 * pow<2>(b) + + f2 * i00 * (2. - 2. * p1) * p1 + + f1 * b * (2. * f2 * i00 * b - i11 * d - i11 * p2)) / + (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); + // Estimate the error components using linearized extrapolation, + // sum in squares. + const auto e10_I00 = pow<2>(mpdi00 * e00); + const auto e10_I11 = pow<2>(mpdi11 * e11); + const auto e10_F1 = pow<2>(mpdf1 * f1E); + const auto e10_F2 = pow<2>(mpdf2 * f2E); + const auto e10_P1 = pow<2>(mpdp1 * p1E); + const auto e10_P2 = pow<2>(mpdp2 * p2E); + return std::sqrt(e10_I00 + e10_I11 + e10_F1 + e10_F2 + e10_P1 + e10_P2); +} +} // namespace + +namespace Mantid { +namespace Algorithms { + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(PolarizationCorrectionWildes) + +//---------------------------------------------------------------------------------------------- + +/// Algorithms name for identification. @see Algorithm::name +const std::string PolarizationCorrectionWildes::name() const { + return "PolarizationCorrectionWildes"; +} + +/// Algorithm's version for identification. @see Algorithm::version +int PolarizationCorrectionWildes::version() const { return 1; } + +/// Algorithm's category for identification. @see Algorithm::category +const std::string PolarizationCorrectionWildes::category() const { + return "Reflectometry"; +} + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string PolarizationCorrectionWildes::summary() const { + return "Corrects a group of polarization analysis workspaces for polarizer " + "and analyzer efficiencies."; +} + +/** + * Count the non-nullptr workspaces + * @return the count on non-nullptr workspaces. + */ +size_t PolarizationCorrectionWildes::WorkspaceMap::size() const noexcept { + return (mmWS ? 1 : 0) + (mpWS ? 1 : 0) + (pmWS ? 1 : 0) + (ppWS ? 1 : 0); +} + +//---------------------------------------------------------------------------------------------- +/** Initialize the algorithm's properties. + */ +void PolarizationCorrectionWildes::init() { + declareProperty(Kernel::make_unique<Kernel::ArrayProperty<std::string>>( + Prop::INPUT_WS, "", + boost::make_shared<API::ADSValidator>(), + Kernel::Direction::Input), + "A list of workspaces to be corrected corresponding to the " + "flipper configurations."); + declareProperty( + Kernel::make_unique<API::WorkspaceProperty<API::WorkspaceGroup>>( + Prop::OUTPUT_WS, "", Kernel::Direction::Output), + "A group of polarization efficiency corrected workspaces."); + const std::string full = Flippers::OffOff + ", " + Flippers::OffOn + ", " + + Flippers::OnOff + ", " + Flippers::OnOn; + const std::string missing01 = + Flippers::OffOff + ", " + Flippers::OnOff + ", " + Flippers::OnOn; + const std::string missing10 = + Flippers::OffOff + ", " + Flippers::OffOn + ", " + Flippers::OnOn; + const std::string missing0110 = Flippers::OffOff + ", " + Flippers::OnOn; + const std::string noAnalyzer = Flippers::Off + ", " + Flippers::On; + const std::string directBeam = Flippers::Off; + const std::vector<std::string> setups{ + {full, missing01, missing10, missing0110, noAnalyzer, directBeam}}; + declareProperty( + Prop::FLIPPERS, full, + boost::make_shared<Kernel::ListValidator<std::string>>(setups), + "Flipper configurations of the input workspaces."); + declareProperty( + Kernel::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>( + Prop::EFFICIENCIES, "", Kernel::Direction::Input), + "A workspace containing the efficiency factors P1, P2, F1 and F2 as " + "histograms"); +} + +//---------------------------------------------------------------------------------------------- +/** Execute the algorithm. + */ +void PolarizationCorrectionWildes::exec() { + const std::string flipperProperty = getProperty(Prop::FLIPPERS); + const auto flippers = parseFlipperSetup(flipperProperty); + const bool analyzer = flippers.front() != "0" && flippers.back() != "1"; + const auto inputs = mapInputsToDirections(flippers); + checkConsistentNumberHistograms(inputs); + const EfficiencyMap efficiencies = efficiencyFactors(); + checkConsistentX(inputs, efficiencies); + WorkspaceMap outputs; + switch (inputs.size()) { + case 1: + outputs = directBeamCorrections(inputs, efficiencies); + break; + case 2: + if (analyzer) { + outputs = twoInputCorrections(inputs, efficiencies); + } else { + outputs = analyzerlessCorrections(inputs, efficiencies); + } + break; + case 3: + outputs = threeInputCorrections(inputs, efficiencies); + break; + case 4: + outputs = fullCorrections(inputs, efficiencies); + } + setProperty(Prop::OUTPUT_WS, groupOutput(outputs)); +} + +/** + * Validate the algorithm's input properties. + * @return a map from property names to discovered issues + */ +std::map<std::string, std::string> +PolarizationCorrectionWildes::validateInputs() { + std::map<std::string, std::string> issues; + API::MatrixWorkspace_const_sptr factorWS = getProperty(Prop::EFFICIENCIES); + const auto &factorAxis = factorWS->getAxis(1); + if (!factorAxis) { + issues[Prop::EFFICIENCIES] = "The workspace is missing a vertical axis."; + } else if (!factorAxis->isText()) { + issues[Prop::EFFICIENCIES] = + "The vertical axis in the workspace is not text axis."; + } else if (factorWS->getNumberHistograms() < 4) { + issues[Prop::EFFICIENCIES] = + "The workspace should contain at least 4 histograms."; + } else { + std::vector<std::string> tags{{"P1", "P2", "F1", "F2"}}; + for (size_t i = 0; i != factorAxis->length(); ++i) { + const auto label = factorAxis->label(i); + auto found = std::find(tags.begin(), tags.end(), label); + if (found != tags.cend()) { + std::swap(tags.back(), *found); + tags.pop_back(); + } + } + if (!tags.empty()) { + issues[Prop::EFFICIENCIES] = "A histogram labeled " + tags.front() + + " is missing from the workspace."; + } + } + const std::vector<std::string> inputs = getProperty(Prop::INPUT_WS); + const std::string flipperProperty = getProperty(Prop::FLIPPERS); + const auto flippers = parseFlipperSetup(flipperProperty); + if (inputs.size() != flippers.size()) { + issues[Prop::FLIPPERS] = + "The number of flipper configurations (" + + std::to_string(flippers.size()) + + ") does not match the number of input workspaces (" + + std::to_string(inputs.size()) + ")"; + } + return issues; +} + +/** + * Check that all workspaces in inputs have the same number of histograms. + * @param inputs a set of workspaces to check + */ +void PolarizationCorrectionWildes::checkConsistentNumberHistograms( + const WorkspaceMap &inputs) { + size_t nHist{0}; + bool nHistValid{false}; + // A local helper function to check the number of histograms. + auto checkNHist = [&nHist, &nHistValid](const API::MatrixWorkspace_sptr &ws, + const std::string &tag) { + if (nHistValid) { + if (nHist != ws->getNumberHistograms()) { + throw std::runtime_error("Number of histograms mismatch in " + tag); + } + } else { + nHist = ws->getNumberHistograms(); + nHistValid = true; + } + }; + if (inputs.mmWS) { + checkNHist(inputs.mmWS, Flippers::OffOff); + } + if (inputs.mpWS) { + checkNHist(inputs.mpWS, Flippers::OffOn); + } + if (inputs.pmWS) { + checkNHist(inputs.pmWS, Flippers::OnOff); + } + if (inputs.ppWS) { + checkNHist(inputs.ppWS, Flippers::OnOn); + } +} + +/** + * Check that all workspaces and efficicencies have the same X data. + * @param inputs a set of workspaces to check + * @param efficiencies efficiencies to check + */ +void PolarizationCorrectionWildes::checkConsistentX( + const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + // Compare everything to F1 efficiency. + const auto &F1x = efficiencies.F1->x(); + // A local helper function to check a HistogramX against F1. + auto checkX = + [&F1x](const HistogramData::HistogramX &x, const std::string &tag) { + if (x.size() != F1x.size()) { + throw std::runtime_error( + "Mismatch of histogram lengths between F1 and " + tag + '.'); + } + for (size_t i = 0; i != x.size(); ++i) { + if (x[i] != F1x[i]) { + throw std::runtime_error("Mismatch of X data between F1 and " + + tag + '.'); + } + } + }; + const auto &F2x = efficiencies.F2->x(); + checkX(F2x, "F2"); + const auto &P1x = efficiencies.P1->x(); + checkX(P1x, "P1"); + const auto &P2x = efficiencies.P2->x(); + checkX(P2x, "P2"); + // A local helper function to check an input workspace against F1. + auto checkWS = + [&checkX](const API::MatrixWorkspace_sptr &ws, const std::string &tag) { + const auto nHist = ws->getNumberHistograms(); + for (size_t i = 0; i != nHist; ++i) { + checkX(ws->x(i), tag); + } + }; + if (inputs.mmWS) { + checkWS(inputs.mmWS, Flippers::OffOff); + } + if (inputs.mpWS) { + checkWS(inputs.mpWS, Flippers::OffOn); + } + if (inputs.pmWS) { + checkWS(inputs.pmWS, Flippers::OnOff); + } + if (inputs.ppWS) { + checkWS(inputs.ppWS, Flippers::OnOn); + } +} + +/** + * Make a workspace group out of the given set of workspaces. + * The workspaces will be published in the ADS, their names appended by + * appropriate suffices. + * @param outputs a set of workspaces to group + * @return a group workspace + */ +API::WorkspaceGroup_sptr +PolarizationCorrectionWildes::groupOutput(const WorkspaceMap &outputs) { + const std::string outWSName = getProperty(Prop::OUTPUT_WS); + std::vector<std::string> names; + if (outputs.mmWS) { + names.emplace_back(outWSName + "_--"); + API::AnalysisDataService::Instance().addOrReplace(names.back(), + outputs.mmWS); + } + if (outputs.mpWS) { + names.emplace_back(outWSName + "_-+"); + API::AnalysisDataService::Instance().addOrReplace(names.back(), + outputs.mpWS); + } + if (outputs.pmWS) { + names.emplace_back(outWSName + "_+-"); + API::AnalysisDataService::Instance().addOrReplace(names.back(), + outputs.pmWS); + } + if (outputs.ppWS) { + names.emplace_back(outWSName + "_++"); + API::AnalysisDataService::Instance().addOrReplace(names.back(), + outputs.ppWS); + } + auto group = createChildAlgorithm("GroupWorkspaces"); + group->initialize(); + group->setProperty("InputWorkspaces", names); + group->setProperty("OutputWorkspace", outWSName); + group->execute(); + API::WorkspaceGroup_sptr outWS = group->getProperty("OutputWorkspace"); + return outWS; +} + +/** + * Make a convenience access object to the efficiency factors. + * @return an EfficiencyMap object + */ +PolarizationCorrectionWildes::EfficiencyMap +PolarizationCorrectionWildes::efficiencyFactors() { + EfficiencyMap e; + API::MatrixWorkspace_const_sptr factorWS = getProperty(Prop::EFFICIENCIES); + const auto &vertAxis = factorWS->getAxis(1); + for (size_t i = 0; i != vertAxis->length(); ++i) { + const auto label = vertAxis->label(i); + if (label == "P1") { + e.P1 = &factorWS->getSpectrum(i); + } else if (label == "P2") { + e.P2 = &factorWS->getSpectrum(i); + } else if (label == "F1") { + e.F1 = &factorWS->getSpectrum(i); + } else if (label == "F2") { + e.F2 = &factorWS->getSpectrum(i); + } + // Ignore other histograms such as 'Phi' in ILL's efficiency ws. + } + return e; +} + +/** + * Correct a direct beam measurement for non-ideal instrument effects. + * Only the non-analyzer, polarizer flipper off case is considered here. + * @param inputs a set of workspaces to correct + * @param efficiencies a set of efficiency factors + * @return set of corrected workspaces + */ +PolarizationCorrectionWildes::WorkspaceMap +PolarizationCorrectionWildes::directBeamCorrections( + const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + using namespace boost::math; + checkInputExists(inputs.ppWS, Flippers::Off); + WorkspaceMap outputs; + outputs.ppWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); + const size_t nHisto = inputs.ppWS->getNumberHistograms(); + for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { + const auto &ppY = inputs.ppWS->y(wsIndex); + const auto &ppE = inputs.ppWS->e(wsIndex); + auto &ppYOut = outputs.ppWS->mutableY(wsIndex); + auto &ppEOut = outputs.ppWS->mutableE(wsIndex); + for (size_t binIndex = 0; binIndex < ppY.size(); ++binIndex) { + const auto P1 = efficiencies.P1->y()[binIndex]; + const auto P2 = efficiencies.P2->y()[binIndex]; + const double f = 1. - P1 - P2 + 2. * P1 * P2; + ppYOut[binIndex] = ppY[binIndex] / f; + const auto P1E = efficiencies.P1->e()[binIndex]; + const auto P2E = efficiencies.P2->e()[binIndex]; + const auto e1 = pow<2>(P1E * (2. * P1 - 1.) / pow<2>(f) * ppY[binIndex]); + const auto e2 = pow<2>(P2E * (2. * P2 - 1.) / pow<2>(f) * ppY[binIndex]); + const auto e3 = pow<2>(ppE[binIndex] / f); + const auto errorSum = std::sqrt(e1 + e2 + e3); + ppEOut[binIndex] = errorSum; + } + } + return outputs; +} + +/** + * Correct for non-ideal instrument effects. + * Deals with the case when the data was taken without the analyzer: + * only the polarizer flipper is used. + * @param inputs a set of workspaces to correct + * @param efficiencies a set of efficiency factors + * @return a set of corrected workspaces + */ +PolarizationCorrectionWildes::WorkspaceMap +PolarizationCorrectionWildes::analyzerlessCorrections( + const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + using namespace boost::math; + checkInputExists(inputs.mmWS, Flippers::On); + checkInputExists(inputs.ppWS, Flippers::Off); + WorkspaceMap outputs; + outputs.mmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mmWS); + outputs.ppWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); + const size_t nHisto = inputs.mmWS->getNumberHistograms(); + for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { + const auto &mmY = inputs.mmWS->y(wsIndex); + const auto &mmE = inputs.mmWS->e(wsIndex); + const auto &ppY = inputs.ppWS->y(wsIndex); + const auto &ppE = inputs.ppWS->e(wsIndex); + auto &mmYOut = outputs.mmWS->mutableY(wsIndex); + auto &mmEOut = outputs.mmWS->mutableE(wsIndex); + auto &ppYOut = outputs.ppWS->mutableY(wsIndex); + auto &ppEOut = outputs.ppWS->mutableE(wsIndex); + for (size_t binIndex = 0; binIndex < mmY.size(); ++binIndex) { + const auto F1 = efficiencies.F1->y()[binIndex]; + const auto P1 = efficiencies.P1->y()[binIndex]; + Eigen::Matrix2d F1m; + F1m << 1., 0., (F1 - 1.) / F1, 1. / F1; + const double divisor = (2. * P1 - 1.); + const double diag = (P1 - 1.) / divisor; + const double off = P1 / divisor; + Eigen::Matrix2d P1m; + P1m << diag, off, off, diag; + const Eigen::Vector2d intensities(ppY[binIndex], mmY[binIndex]); + const auto PFProduct = P1m * F1m; + const auto corrected = PFProduct * intensities; + ppYOut[binIndex] = corrected[0]; + mmYOut[binIndex] = corrected[1]; + const auto F1E = efficiencies.F1->e()[binIndex]; + const auto P1E = efficiencies.P1->e()[binIndex]; + const auto elemE1 = -1. / pow<2>(F1) * F1E; + Eigen::Matrix2d F1Em; + F1Em << 0., 0., -elemE1, elemE1; + const auto elemE2 = 1. / pow<2>(divisor) * P1E; + Eigen::Matrix2d P1Em; + P1Em << elemE2, -elemE2, -elemE2, elemE2; + const Eigen::Vector2d errors(ppE[binIndex], mmE[binIndex]); + const auto e1 = (P1Em * F1m * intensities).array(); + const auto e2 = (P1m * F1Em * intensities).array(); + const auto sqPFProduct = (PFProduct.array() * PFProduct.array()).matrix(); + const auto sqErrors = (errors.array() * errors.array()).matrix(); + const auto e3 = (sqPFProduct * sqErrors).array(); + const auto errorSum = (e1 * e1 + e2 * e2 + e3).sqrt(); + ppEOut[binIndex] = errorSum[0]; + mmEOut[binIndex] = errorSum[1]; + } + } + return outputs; +} + +/** + * Correct for non-ideal instrument effects. + * Only 00 and 11 flipper configurations need to be provided; + * the missing 01 and 10 data is solved from the assumption that + * in the corrected data, R01 = R10 = 0. + * @param inputs a set of workspaces to correct + * @param efficiencies a set of efficiency factors + * @return a set of corrected workspaces + */ +PolarizationCorrectionWildes::WorkspaceMap +PolarizationCorrectionWildes::twoInputCorrections( + const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + using namespace boost::math; + checkInputExists(inputs.mmWS, Flippers::OnOn); + checkInputExists(inputs.ppWS, Flippers::OffOff); + WorkspaceMap fullInputs = inputs; + fullInputs.mpWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mmWS); + fullInputs.pmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); + twoInputsSolve01And10(fullInputs, inputs, efficiencies); + return fullCorrections(fullInputs, efficiencies); +} + +/** + * Correct for non-ideal instrument effects. + * Needs the 00 and 11 flipper configurations as well as either 01 or 10. + * The missing intensity (01 or 10) is solved from the assumption + * that the corrected R01 = R10. + * @param inputs a set of workspaces to correct + * @param efficiencies a set of efficiency factors + * @return a set of corrected workspaces + */ +PolarizationCorrectionWildes::WorkspaceMap +PolarizationCorrectionWildes::threeInputCorrections( + const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + WorkspaceMap fullInputs = inputs; + checkInputExists(inputs.mmWS, Flippers::OnOn); + checkInputExists(inputs.ppWS, Flippers::OffOff); + if (!inputs.mpWS) { + checkInputExists(inputs.pmWS, Flippers::OffOn); + threeInputsSolve10(fullInputs, efficiencies); + } else { + checkInputExists(inputs.mpWS, Flippers::OnOff); + threeInputsSolve01(fullInputs, efficiencies); + } + return fullCorrections(fullInputs, efficiencies); +} + +/** + * Correct for non-ideal instrument effects. + * Perform full polarization corrections. All flipper configurations + * (00, 01, 10 and 11) are needed for this. + * @param inputs a set of workspaces to correct + * @param efficiencies a set of efficiency factors + * @return a set of corrected workspaces + */ +PolarizationCorrectionWildes::WorkspaceMap +PolarizationCorrectionWildes::fullCorrections( + const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + using namespace boost::math; + checkInputExists(inputs.mmWS, Flippers::OnOn); + checkInputExists(inputs.mpWS, Flippers::OnOff); + checkInputExists(inputs.pmWS, Flippers::OffOn); + checkInputExists(inputs.ppWS, Flippers::OffOff); + WorkspaceMap outputs; + outputs.mmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mmWS); + outputs.mpWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mpWS); + outputs.pmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.pmWS); + outputs.ppWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); + const auto F1 = efficiencies.F1->y(); + const auto F1E = efficiencies.F1->e(); + const auto F2 = efficiencies.F2->y(); + const auto F2E = efficiencies.F2->e(); + const auto P1 = efficiencies.P1->y(); + const auto P1E = efficiencies.P1->e(); + const auto P2 = efficiencies.P2->y(); + const auto P2E = efficiencies.P2->e(); + const size_t nHisto = inputs.mmWS->getNumberHistograms(); + for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { + const auto &mmY = inputs.mmWS->y(wsIndex); + const auto &mmE = inputs.mmWS->e(wsIndex); + const auto &mpY = inputs.mpWS->y(wsIndex); + const auto &mpE = inputs.mpWS->e(wsIndex); + const auto &pmY = inputs.pmWS->y(wsIndex); + const auto &pmE = inputs.pmWS->e(wsIndex); + const auto &ppY = inputs.ppWS->y(wsIndex); + const auto &ppE = inputs.ppWS->e(wsIndex); + auto &mmYOut = outputs.mmWS->mutableY(wsIndex); + auto &mmEOut = outputs.mmWS->mutableE(wsIndex); + auto &mpYOut = outputs.mpWS->mutableY(wsIndex); + auto &mpEOut = outputs.mpWS->mutableE(wsIndex); + auto &pmYOut = outputs.pmWS->mutableY(wsIndex); + auto &pmEOut = outputs.pmWS->mutableE(wsIndex); + auto &ppYOut = outputs.ppWS->mutableY(wsIndex); + auto &ppEOut = outputs.ppWS->mutableE(wsIndex); + for (size_t binIndex = 0; binIndex < mmY.size(); ++binIndex) { + Eigen::Vector4d corrected; + Eigen::Vector4d errors; + fourInputsCorrectedAndErrors(corrected, errors, ppY[binIndex], + ppE[binIndex], pmY[binIndex], pmE[binIndex], + mpY[binIndex], mpE[binIndex], mmY[binIndex], + mmE[binIndex], F1[binIndex], F1E[binIndex], + F2[binIndex], F2E[binIndex], P1[binIndex], + P1E[binIndex], P2[binIndex], P2E[binIndex]); + ppYOut[binIndex] = corrected[0]; + pmYOut[binIndex] = corrected[1]; + mpYOut[binIndex] = corrected[2]; + mmYOut[binIndex] = corrected[3]; + ppEOut[binIndex] = errors[0]; + pmEOut[binIndex] = errors[1]; + mpEOut[binIndex] = errors[2]; + mmEOut[binIndex] = errors[3]; + } + } + return outputs; +} + +/** + * Make a set of workspaces to correct from input properties. + * @param flippers a vector of flipper configurations + * @return a set of workspaces to correct + */ +PolarizationCorrectionWildes::WorkspaceMap +PolarizationCorrectionWildes::mapInputsToDirections( + const std::vector<std::string> &flippers) { + const std::vector<std::string> inputNames = getProperty(Prop::INPUT_WS); + WorkspaceMap inputs; + for (size_t i = 0; i < flippers.size(); ++i) { + auto ws = + (API::AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>( + inputNames[i])); + if (!ws) { + throw std::runtime_error( + "One of the input workspaces doesn't seem to be a MatrixWorkspace."); + } + const auto &f = flippers[i]; + if (f == Flippers::OnOn || f == Flippers::On) { + inputs.mmWS = ws; + } else if (f == Flippers::OnOff) { + inputs.mpWS = ws; + } else if (f == Flippers::OffOn) { + inputs.pmWS = ws; + } else if (f == Flippers::OffOff || f == Flippers::Off) { + inputs.ppWS = ws; + } else { + throw std::runtime_error(std::string{"Unknown entry in "} + + Prop::FLIPPERS); + } + } + return inputs; +} + +/** + * Solve in-place the 01 flipper configuration from the assumption that + * for the corrected intensities, R01 = R10. + * @param inputs a set of input workspaces + * @param efficiencies a set of efficiency factors + */ +void PolarizationCorrectionWildes::threeInputsSolve01( + WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + using namespace Mantid::DataObjects; + inputs.pmWS = create<Workspace2D>(*inputs.mpWS); + const auto &F1 = efficiencies.F1->y(); + const auto &F2 = efficiencies.F2->y(); + const auto &P1 = efficiencies.P1->y(); + const auto &P2 = efficiencies.P2->y(); + const auto nHisto = inputs.pmWS->getNumberHistograms(); + for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { + const auto &I00 = inputs.ppWS->y(wsIndex); + auto &I01 = inputs.pmWS->mutableY(wsIndex); + const auto &I10 = inputs.mpWS->y(wsIndex); + const auto &I11 = inputs.mmWS->y(wsIndex); + for (size_t binIndex = 0; binIndex != I00.size(); ++binIndex) { + const auto f1 = F1[binIndex]; + const auto f2 = F2[binIndex]; + const auto p1 = P1[binIndex]; + const auto p2 = P2[binIndex]; + const auto i00 = I00[binIndex]; + const auto i10 = I10[binIndex]; + const auto i11 = I11[binIndex]; + I01[binIndex] = + (f1 * i00 * (-1. + 2. * p1) - (i00 - i10 + i11) * (p1 - p2) - + f2 * (i00 - i10) * (-1. + 2. * p2)) / + (-p1 + f1 * (-1. + 2. * p1) + p2); + // The errors are left to zero. + } + } +} + +/** + * Solve in-place the 10 flipper configuration from the assumption that + * for the corrected intensities R01 = R10. + * @param inputs a set of input workspaces + * @param efficiencies a set of efficiency factors + */ +void PolarizationCorrectionWildes::threeInputsSolve10( + WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { + inputs.mpWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.pmWS); + const auto &F1 = efficiencies.F1->y(); + const auto &F2 = efficiencies.F2->y(); + const auto &P1 = efficiencies.P1->y(); + const auto &P2 = efficiencies.P2->y(); + const auto nHisto = inputs.mpWS->getNumberHistograms(); + for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { + const auto &I00 = inputs.ppWS->y(wsIndex); + const auto &I01 = inputs.pmWS->y(wsIndex); + auto &I10 = inputs.mpWS->mutableY(wsIndex); + const auto &I11 = inputs.mmWS->y(wsIndex); + for (size_t binIndex = 0; binIndex != I00.size(); ++binIndex) { + const auto f1 = F1[binIndex]; + const auto f2 = F2[binIndex]; + const auto p1 = P1[binIndex]; + const auto p2 = P2[binIndex]; + const auto i00 = I00[binIndex]; + const auto i01 = I01[binIndex]; + const auto i11 = I11[binIndex]; + I10[binIndex] = + (-f1 * (i00 - i01) * (-1. + 2. * p1) + (i00 - i01 + i11) * (p1 - p2) + + f2 * i00 * (-1. + 2. * p2)) / + (p1 - p2 + f2 * (-1. + 2. * p2)); + // The errors are left to zero. + } + } +} + +/** + * Solve in-place the 01 and 10 flipper configurations from the assumption that + * for the corrected intensities R01 = R10 = 0. + * @param fullInputs a set of output workspaces + * @param inputs a set of input workspaces + * @param efficiencies a set of efficiency factors + */ +void PolarizationCorrectionWildes::twoInputsSolve01And10( + WorkspaceMap &fullInputs, const WorkspaceMap &inputs, + const EfficiencyMap &efficiencies) { + using namespace boost::math; + const auto &F1 = efficiencies.F1->y(); + const auto &F1E = efficiencies.F1->e(); + const auto &F2 = efficiencies.F2->y(); + const auto &F2E = efficiencies.F2->e(); + const auto &P1 = efficiencies.P1->y(); + const auto &P1E = efficiencies.P1->e(); + const auto &P2 = efficiencies.P2->y(); + const auto &P2E = efficiencies.P2->e(); + const auto nHisto = inputs.mmWS->getNumberHistograms(); + for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { + const auto &I00 = inputs.ppWS->y(wsIndex); + const auto &E00 = inputs.ppWS->e(wsIndex); + const auto &I11 = inputs.mmWS->y(wsIndex); + const auto &E11 = inputs.mmWS->e(wsIndex); + auto &I01 = fullInputs.pmWS->mutableY(wsIndex); + auto &E01 = fullInputs.pmWS->mutableE(wsIndex); + auto &I10 = fullInputs.mpWS->mutableY(wsIndex); + auto &E10 = fullInputs.mpWS->mutableE(wsIndex); + for (size_t binIndex = 0; binIndex != I00.size(); ++binIndex) { + const auto i00 = I00[binIndex]; + const auto i11 = I11[binIndex]; + const auto f1 = F1[binIndex]; + const auto f2 = F2[binIndex]; + const auto p1 = P1[binIndex]; + const auto p2 = P2[binIndex]; + const auto a = -1. + p1 + 2. * p2 - 2. * p1 * p2; + const auto b = -1. + 2. * p1; + const auto c = -1. + 2. * p2; + const auto d = -1. + p2; + // Case: 01 + const auto divisor = f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c); + I01[binIndex] = + (f2 * i11 * p1 * a - + f1 * i00 * b * (-f2 * pow<2>(c) + pow<2>(f2 * c) + d * p2)) / + divisor; + E01[binIndex] = twoInputsErrorEstimate01( + i00, E00[binIndex], i11, E11[binIndex], p1, P1E[binIndex], p2, + P2E[binIndex], f1, F1E[binIndex], f2, F2E[binIndex]); + // Case: 10 + I10[binIndex] = + (-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + + f1 * b * (-i11 * d * p2 + f2 * i00 * b * c)) / + divisor; + E10[binIndex] = twoInputsErrorEstimate10( + i00, E00[binIndex], i11, E11[binIndex], p1, P1E[binIndex], p2, + P2E[binIndex], f1, F1E[binIndex], f2, F2E[binIndex]); + } + } +} +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/Algorithms/src/PolarizationEfficiencyCor.cpp b/Framework/Algorithms/src/PolarizationEfficiencyCor.cpp index 8a143b35f67b0825a31152b1d68be1cbaafeb94f..44b10eae96c3cad947d1f2e5591b1ebf26579422 100644 --- a/Framework/Algorithms/src/PolarizationEfficiencyCor.cpp +++ b/Framework/Algorithms/src/PolarizationEfficiencyCor.cpp @@ -10,16 +10,22 @@ #include "MantidKernel/ListValidator.h" #include "MantidKernel/StringTokenizer.h" +#include "MantidAPI/WorkspaceFactory.h" + #include <Eigen/Dense> #include <boost/math/special_functions/pow.hpp> namespace { + /// Property names. namespace Prop { static const std::string FLIPPERS{"Flippers"}; +static const std::string POLARIZATION_ANALYSIS{"PolarizationAnalysis"}; static const std::string EFFICIENCIES{"Efficiencies"}; -static const std::string INPUT_WS{"InputWorkspaces"}; -static const std::string OUTPUT_WS{"OutputWorkspace"}; +static const std::string INPUT_WORKSPACES{"InputWorkspaces"}; +static const std::string INPUT_WORKSPACE_GROUP{"InputWorkspaceGroup"}; +static const std::string OUTPUT_WORKSPACES{"OutputWorkspace"}; +static const std::string CORRECTION_METHOD{"CorrectionMethod"}; } // namespace Prop /// Flipper configurations. @@ -32,326 +38,19 @@ static const std::string OnOff{"10"}; static const std::string OnOn{"11"}; } // namespace Flippers -/** - * Parse a flipper configuration string. - * @param setupString a configuration string - * @return a vector of individual configurations - */ -std::vector<std::string> parseFlipperSetup(const std::string &setupString) { - using Mantid::Kernel::StringTokenizer; - StringTokenizer tokens{setupString, ",", StringTokenizer::TOK_TRIM}; - return std::vector<std::string>{tokens.begin(), tokens.end()}; -} - -/** - * Throw if given ws is nullptr. - * @param ws a workspace to check - * @param tag a flipper configuration for the error message - */ -void checkInputExists(const Mantid::API::MatrixWorkspace_sptr &ws, - const std::string &tag) { - if (!ws) { - throw std::runtime_error("A workspace designated as " + tag + - " is missing in inputs."); - } -} - -/** - * Calculate the corrected intensities and error estimates. - * @param corrected an output vector for R00, R01, R10 and R11 - * @param errors an output vector for the error estimates - * @param ppy intensity I00 - * @param ppyE error of ppy - * @param pmy intensity I01 - * @param pmyE error of pmy - * @param mpy intensity I10 - * @param mpyE error of mpy - * @param mmy intensity I11 - * @param mmyE error of mmy - * @param f1 polarizer efficiency - * @param f1E error of f1 - * @param f2 analyzer efficiency - * @param f2E error of f2 - * @param p1 polarizer flipper efficiency - * @param p1E error of p1 - * @param p2 analyzer flipper efficiency - * @param p2E error of p2 - */ -void fourInputsCorrectedAndErrors( - Eigen::Vector4d &corrected, Eigen::Vector4d &errors, const double ppy, - const double ppyE, const double pmy, const double pmyE, const double mpy, - const double mpyE, const double mmy, const double mmyE, const double f1, - const double f1E, const double f2, const double f2E, const double p1, - const double p1E, const double p2, const double p2E) { - using namespace boost::math; - // Note that f1 and f2 correspond to 1-F1 and 1-F2 in [Wildes, 1999]. - // These are inverted forms of the efficiency matrices. - const auto diag1 = 1. / f1; - const auto off1 = (f1 - 1.) / f1; - Eigen::Matrix4d F1m; - F1m << 1., 0., 0., 0., 0., 1., 0., 0., off1, 0., diag1, 0., 0., off1, 0., - diag1; - const auto diag2 = 1. / f2; - const auto off2 = (f2 - 1.) / f2; - Eigen::Matrix4d F2m; - F2m << 1., 0., 0., 0., off2, diag2, 0., 0., 0., 0., 1., 0., 0., 0., off2, - diag2; - const auto diag3 = (p1 - 1.) / (2. * p1 - 1.); - const auto off3 = p1 / (2. * p1 - 1); - Eigen::Matrix4d P1m; - P1m << diag3, 0, off3, 0, 0, diag3, 0, off3, off3, 0, diag3, 0, 0, off3, 0, - diag3; - const auto diag4 = (p2 - 1.) / (2. * p2 - 1.); - const auto off4 = p2 / (2. * p2 - 1.); - Eigen::Matrix4d P2m; - P2m << diag4, off4, 0., 0., off4, diag4, 0., 0., 0., 0., diag4, off4, 0., 0., - off4, diag4; - const Eigen::Vector4d intensities(ppy, pmy, mpy, mmy); - const auto FProduct = F2m * F1m; - const auto PProduct = P2m * P1m; - const auto PFProduct = PProduct * FProduct; - corrected = PFProduct * intensities; - // The error matrices here are element-wise algebraic derivatives of - // the matrices above, multiplied by the error. - const auto elemE1 = -1. / pow<2>(f1) * f1E; - Eigen::Matrix4d F1Em; - F1Em << 0., 0., 0., 0., 0., 0., 0., 0., -elemE1, 0., elemE1, 0., 0., -elemE1, - 0., elemE1; - const auto elemE2 = -1. / pow<2>(f2) * f2E; - Eigen::Matrix4d F2Em; - F2Em << 0., 0., 0., 0., -elemE2, elemE2, 0., 0., 0., 0., 0., 0., 0., 0., - -elemE2, elemE2; - const auto elemE3 = 1. / pow<2>(2. * p1 - 1.) * p1E; - Eigen::Matrix4d P1Em; - P1Em << elemE3, 0., -elemE3, 0., 0., elemE3, 0., -elemE3, -elemE3, 0., elemE3, - 0., 0., -elemE3, 0., elemE3; - const auto elemE4 = 1. / pow<2>(2. * p2 - 1.) * p2E; - Eigen::Matrix4d P2Em; - P2Em << elemE4, -elemE4, 0., 0., -elemE4, elemE4, 0., 0., 0., 0., elemE4, - -elemE4, 0., 0., -elemE4, elemE4; - const Eigen::Vector4d yErrors(ppyE, pmyE, mpyE, mmyE); - const auto e1 = (P2Em * P1m * FProduct * intensities).array(); - const auto e2 = (P2m * P1Em * FProduct * intensities).array(); - const auto e3 = (PProduct * F2Em * F1m * intensities).array(); - const auto e4 = (PProduct * F2m * F1Em * intensities).array(); - const auto sqPFProduct = (PFProduct.array() * PFProduct.array()).matrix(); - const auto sqErrors = (yErrors.array() * yErrors.array()).matrix(); - const auto e5 = (sqPFProduct * sqErrors).array(); - errors = (e1 * e1 + e2 * e2 + e3 * e3 + e4 * e4 + e5).sqrt(); -} +namespace CorrectionMethod { +static const std::string WILDES{"Wildes"}; +static const std::string FREDRIKZE{"Fredrikze"}; +} // namespace CorrectionMethod -/** - * Estimate errors for I01 in the two inputs case. - * @param i00 intensity of 00 flipper configuration - * @param e00 error of i00 - * @param i11 intensity of 11 flipper configuration - * @param e11 error of i11 - * @param p1 polarizer efficiency - * @param p1E error of p1 - * @param p2 analyzer efficiency - * @param p2E error of p2 - * @param f1 polarizer flipper efficiency - * @param f1E error of f1 - * @param f2 analyzer flipper efficiency - * @param f2E error of f2 - * @return the error estimate - */ -double twoInputsErrorEstimate01(const double i00, const double e00, - const double i11, const double e11, - const double p1, const double p1E, - const double p2, const double p2E, - const double f1, const double f1E, - const double f2, const double f2E) { - using namespace boost::math; - // Derivatives of the equation which solves the I01 intensities - // with respect to i00, i11, f1, etc. - const auto pmdi00 = - -((f1 * (-1. + 2. * p1) * - (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + - (-1. + p2) * p2)) / - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))); - const auto pmdi11 = - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2)) / - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); - const auto pmdf1 = - -(((-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)) * - (f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - - f1 * i00 * (-1. + 2. * p1) * - (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + - (-1. + p2) * p2))) / - pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) - - (i00 * (-1. + 2. * p1) * - (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + - (-1. + p2) * p2)) / - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); - const auto pmdf2 = - -(((f1 * (-1. + 2. * p1) * (-1. + p1 + p2) * (-1 + 2 * p2) + - p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2)) * - (f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - - f1 * i00 * (-1. + 2. * p1) * - (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + - (-1. + p2) * p2))) / - pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + - (-f1 * i00 * (-1. + 2. * p1) * - (-pow<2>(1. - 2. * p2) + 2 * f2 * pow<2>(1. - 2. * p2)) + - i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2)) / - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); - const auto pmdp1 = - -(((f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - - f1 * i00 * (-1. + 2. * p1) * - (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + - (-1. + p2) * p2)) * - (f2 * p1 * (1. - 2. * p2) + - f1 * f2 * (-1. + 2. * p1) * (-1. + 2. * p2) + - f2 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - 2. * f1 * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) / - pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + - (f2 * i11 * p1 * (1. - 2. * p2) + - f2 * i11 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - - 2. * f1 * i00 * (-f2 * pow<2>(1. - 2. * p2) + - pow<2>(f2) * pow<2>(1. - 2. * p2) + (-1. + p2) * p2)) / - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); - const auto pmdp2 = - -(((f2 * i11 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) - - f1 * i00 * (-1. + 2. * p1) * - (-f2 * pow<2>(1. - 2. * p2) + pow<2>(f2) * pow<2>(1. - 2. * p2) + - (-1. + p2) * p2)) * - (f2 * (2. - 2. * p1) * p1 + - f1 * (-1. + 2. * p1) * (1. - 2. * p2 + 2. * f2 * (-1. + p1 + p2) + - f2 * (-1. + 2. * p2)))) / - pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + - (f2 * i11 * (2. - 2. * p1) * p1 - - f1 * i00 * (-1. + 2. * p1) * - (-1. + 4. * f2 * (1. - 2. * p2) - 4. * pow<2>(f2) * (1. - 2. * p2) + - 2. * p2)) / - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); - // Estimate the error components using linearized extrapolation, - // sum in squares. - const auto e01_I00 = pow<2>(pmdi00 * e00); - const auto e01_I11 = pow<2>(pmdi11 * e11); - const auto e01_F1 = pow<2>(pmdf1 * f1E); - const auto e01_F2 = pow<2>(pmdf2 * f2E); - const auto e01_P1 = pow<2>(pmdp1 * p1E); - const auto e01_P2 = pow<2>(pmdp2 * p2E); - return std::sqrt(e01_I00 + e01_I11 + e01_F1 + e01_F2 + e01_P1 + e01_P2); -} - -/** - * Estimate errors for I10 in the two inputs case. - * @param i00 intensity of 00 flipper configuration - * @param e00 error of i00 - * @param i11 intensity of 11 flipper configuration - * @param e11 error of i11 - * @param p1 polarizer efficiency - * @param p1E error of p1 - * @param p2 analyzer efficiency - * @param p2E error of p2 - * @param f1 polarizer flipper efficiency - * @param f1E error of f1 - * @param f2 analyzer flipper efficiency - * @param f2E error of f2 - * @return the error estimate - */ -double twoInputsErrorEstimate10(const double i00, const double e00, - const double i11, const double e11, - const double p1, const double p1E, - const double p2, const double p2E, - const double f1, const double f1E, - const double f2, const double f2E) { - using namespace boost::math; - // Derivatives of the equation which solves the I10 intensities - // with respect to i00, i11, f1, etc. - const auto a = -1. + p1 + 2. * p2 - 2. * p1 * p2; - const auto b = -1. + 2. * p1; - const auto c = -1. + 2. * p2; - const auto d = -1. + p2; - const auto mpdi00 = (-pow<2>(f1) * f2 * pow<2>(b) * c + - f1 * f2 * pow<2>(b) * c + f2 * p1 * a) / - (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); - const auto mpdi11 = -((f1 * b * d * p2) / - (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))); - const auto mpdf1 = - -(((-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)) * - (-pow<2>(f1) * f2 * i00 * pow<2>(1. - 2. * p1) * (-1. + 2. * p2) + - f2 * i00 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - (-i11 * (-1. + p2) * p2 + - f2 * i00 * (-1. + 2. * p1) * (-1. + 2. * p2)))) / - pow<2>(f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2)))) + - (-2. * f1 * f2 * i00 * pow<2>(1. - 2. * p1) * (-1. + 2. * p2) + - (-1. + 2. * p1) * (-i11 * (-1. + p2) * p2 + - f2 * i00 * (-1. + 2. * p1) * (-1. + 2. * p2))) / - (f2 * p1 * (-1. + p1 + 2. * p2 - 2. * p1 * p2) + - f1 * (-1. + 2. * p1) * - ((1. - p2) * p2 + f2 * (-1. + p1 + p2) * (-1. + 2. * p2))); - const auto mpdf2 = - -(((f1 * b * (p1 + d) * c + p1 * a) * - (-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + - f1 * b * (-i11 * d * p2 + f2 * i00 * b * c))) / - pow<2>(f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))) + - (-pow<2>(f1) * i00 * pow<2>(b) * c + f1 * i00 * pow<2>(b) * c + - i00 * p1 * a) / - (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); - const auto mpdp1 = - -(((-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + - f1 * b * (-i11 * d * p2 + f2 * i00 * b * c)) * - (f2 * p1 * -c + f1 * f2 * b * c + f2 * a + - 2. * f1 * (-d * p2 + f2 * (p1 + d) * c))) / - pow<2>(f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))) + - (f2 * i00 * p1 * -c + 4. * pow<2>(f1) * f2 * i00 * -b * c + - 2. * f1 * f2 * i00 * b * c + f2 * i00 * a + - 2. * f1 * (-i11 * d * p2 + f2 * i00 * b * c)) / - (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); - const auto mpdp2 = - -(((f2 * (2. - 2. * p1) * p1 + - f1 * b * (1. - 2. * p2 + 2. * f2 * (p1 + d) + f2 * c)) * - (-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + - f1 * b * (-i11 * d * p2 + f2 * i00 * b * c))) / - pow<2>(f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c))) + - (-2. * pow<2>(f1) * f2 * i00 * pow<2>(b) + - f2 * i00 * (2. - 2. * p1) * p1 + - f1 * b * (2. * f2 * i00 * b - i11 * d - i11 * p2)) / - (f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c)); - // Estimate the error components using linearized extrapolation, - // sum in squares. - const auto e10_I00 = pow<2>(mpdi00 * e00); - const auto e10_I11 = pow<2>(mpdi11 * e11); - const auto e10_F1 = pow<2>(mpdf1 * f1E); - const auto e10_F2 = pow<2>(mpdf2 * f2E); - const auto e10_P1 = pow<2>(mpdp1 * p1E); - const auto e10_P2 = pow<2>(mpdp2 * p2E); - return std::sqrt(e10_I00 + e10_I11 + e10_F1 + e10_F2 + e10_P1 + e10_P2); -} } // namespace namespace Mantid { namespace Algorithms { +using namespace API; +using namespace Kernel; + // Register the algorithm into the AlgorithmFactory DECLARE_ALGORITHM(PolarizationEfficiencyCor) @@ -376,28 +75,37 @@ const std::string PolarizationEfficiencyCor::summary() const { "and analyzer efficiencies."; } -/** - * Count the non-nullptr workspaces - * @return the count on non-nullptr workspaces. - */ -size_t PolarizationEfficiencyCor::WorkspaceMap::size() const noexcept { - return (mmWS ? 1 : 0) + (mpWS ? 1 : 0) + (pmWS ? 1 : 0) + (ppWS ? 1 : 0); -} - //---------------------------------------------------------------------------------------------- /** Initialize the algorithm's properties. */ void PolarizationEfficiencyCor::init() { - declareProperty(Kernel::make_unique<Kernel::ArrayProperty<std::string>>( - Prop::INPUT_WS, "", - boost::make_shared<API::ADSValidator>(), - Kernel::Direction::Input), - "A list of workspaces to be corrected corresponding to the " - "flipper configurations."); + bool const allowMultiSelection = true; + bool const isOptional = true; + declareProperty( + Kernel::make_unique<Kernel::ArrayProperty<std::string>>( + Prop::INPUT_WORKSPACES, "", + boost::make_shared<ADSValidator>(allowMultiSelection, isOptional), + Kernel::Direction::Input), + "A list of names of workspaces to be corrected."); + + declareProperty(Kernel::make_unique<WorkspaceProperty<WorkspaceGroup>>( + Prop::INPUT_WORKSPACE_GROUP, "", Kernel::Direction::Input, + PropertyMode::Optional), + "A group of workspaces to be corrected."); + + const std::vector<std::string> methods{CorrectionMethod::WILDES, + CorrectionMethod::FREDRIKZE}; declareProperty( - Kernel::make_unique<API::WorkspaceProperty<API::WorkspaceGroup>>( - Prop::OUTPUT_WS, "", Kernel::Direction::Output), - "A group of polarization efficiency corrected workspaces."); + Prop::CORRECTION_METHOD, CorrectionMethod::WILDES, + boost::make_shared<Kernel::ListValidator<std::string>>(methods), + "Correction method."); + + declareProperty(Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + Prop::EFFICIENCIES, "", Kernel::Direction::Input), + "A workspace containing the efficiency factors as " + "histograms: P1, P2, F1 and F2 in the Wildes method and Pp, " + "Ap, Rho and Alpha for Fredrikze."); + const std::string full = Flippers::OffOff + ", " + Flippers::OffOn + ", " + Flippers::OnOff + ", " + Flippers::OnOn; const std::string missing01 = @@ -408,625 +116,233 @@ void PolarizationEfficiencyCor::init() { const std::string noAnalyzer = Flippers::Off + ", " + Flippers::On; const std::string directBeam = Flippers::Off; const std::vector<std::string> setups{ - {full, missing01, missing10, missing0110, noAnalyzer, directBeam}}; + {"", full, missing01, missing10, missing0110, noAnalyzer, directBeam}}; declareProperty( - Prop::FLIPPERS, full, + Prop::FLIPPERS, "", boost::make_shared<Kernel::ListValidator<std::string>>(setups), - "Flipper configurations of the input workspaces."); - declareProperty( - Kernel::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>( - Prop::EFFICIENCIES, "", Kernel::Direction::Input), - "A workspace containing the efficiency factors P1, P2, F1 and F2 as " - "histograms"); + "Flipper configurations of the input workspaces (Wildes method only)"); + + std::vector<std::string> propOptions{"", "PA", "PNR"}; + declareProperty("PolarizationAnalysis", "", + boost::make_shared<StringListValidator>(propOptions), + "What Polarization mode will be used?\n" + "PNR: Polarized Neutron Reflectivity mode\n" + "PA: Full Polarization Analysis PNR-PA " + "(Fredrikze method only)"); + + declareProperty(Kernel::make_unique<WorkspaceProperty<WorkspaceGroup>>( + Prop::OUTPUT_WORKSPACES, "", Kernel::Direction::Output), + "A group of polarization efficiency corrected workspaces."); } //---------------------------------------------------------------------------------------------- /** Execute the algorithm. */ void PolarizationEfficiencyCor::exec() { - const std::string flipperProperty = getProperty(Prop::FLIPPERS); - const auto flippers = parseFlipperSetup(flipperProperty); - const bool analyzer = flippers.front() != "0" && flippers.back() != "1"; - const auto inputs = mapInputsToDirections(flippers); - checkConsistentNumberHistograms(inputs); - const EfficiencyMap efficiencies = efficiencyFactors(); - checkConsistentX(inputs, efficiencies); - WorkspaceMap outputs; - switch (inputs.size()) { - case 1: - outputs = directBeamCorrections(inputs, efficiencies); - break; - case 2: - if (analyzer) { - outputs = twoInputCorrections(inputs, efficiencies); - } else { - outputs = analyzerlessCorrections(inputs, efficiencies); - } - break; - case 3: - outputs = threeInputCorrections(inputs, efficiencies); - break; - case 4: - outputs = fullCorrections(inputs, efficiencies); - } - setProperty(Prop::OUTPUT_WS, groupOutput(outputs)); -} - -/** - * Validate the algorithm's input properties. - * @return a map from property names to discovered issues - */ -std::map<std::string, std::string> PolarizationEfficiencyCor::validateInputs() { - std::map<std::string, std::string> issues; - API::MatrixWorkspace_const_sptr factorWS = getProperty(Prop::EFFICIENCIES); - const auto &factorAxis = factorWS->getAxis(1); - if (!factorAxis) { - issues[Prop::EFFICIENCIES] = "The workspace is missing a vertical axis."; - } else if (!factorAxis->isText()) { - issues[Prop::EFFICIENCIES] = - "The vertical axis in the workspace is not text axis."; - } else if (factorWS->getNumberHistograms() < 4) { - issues[Prop::EFFICIENCIES] = - "The workspace should contain at least 4 histograms."; + std::string const method = getProperty(Prop::CORRECTION_METHOD); + if (method == CorrectionMethod::WILDES) { + execWildes(); } else { - std::vector<std::string> tags{{"P1", "P2", "F1", "F2"}}; - for (size_t i = 0; i != factorAxis->length(); ++i) { - const auto label = factorAxis->label(i); - auto found = std::find(tags.begin(), tags.end(), label); - if (found != tags.cend()) { - std::swap(tags.back(), *found); - tags.pop_back(); - } - } - if (!tags.empty()) { - issues[Prop::EFFICIENCIES] = "A histogram labeled " + tags.front() + - " is missing from the workspace."; - } + execFredrikze(); } - const std::vector<std::string> inputs = getProperty(Prop::INPUT_WS); - const std::string flipperProperty = getProperty(Prop::FLIPPERS); - const auto flippers = parseFlipperSetup(flipperProperty); - if (inputs.size() != flippers.size()) { - issues[Prop::FLIPPERS] = "The number of flipper configurations does not " - "match the number of input workspaces"; - } - return issues; } -/** - * Check that all workspaces in inputs have the same number of histograms. - * @param inputs a set of workspaces to check - */ -void PolarizationEfficiencyCor::checkConsistentNumberHistograms( - const WorkspaceMap &inputs) { - size_t nHist{0}; - bool nHistValid{false}; - // A local helper function to check the number of histograms. - auto checkNHist = [&nHist, &nHistValid](const API::MatrixWorkspace_sptr &ws, - const std::string &tag) { - if (nHistValid) { - if (nHist != ws->getNumberHistograms()) { - throw std::runtime_error("Number of histograms mismatch in " + tag); - } - } else { - nHist = ws->getNumberHistograms(); - nHistValid = true; - } - }; - if (inputs.mmWS) { - checkNHist(inputs.mmWS, Flippers::OffOff); - } - if (inputs.mpWS) { - checkNHist(inputs.mpWS, Flippers::OffOn); - } - if (inputs.pmWS) { - checkNHist(inputs.pmWS, Flippers::OnOff); - } - if (inputs.ppWS) { - checkNHist(inputs.ppWS, Flippers::OnOn); +//---------------------------------------------------------------------------------------------- +void PolarizationEfficiencyCor::execWildes() { + checkWildesProperties(); + std::vector<std::string> workspaces = getWorkspaceNameList(); + + MatrixWorkspace_sptr efficiencies = getEfficiencies(); + auto alg = createChildAlgorithm("PolarizationCorrectionWildes"); + alg->initialize(); + alg->setProperty("InputWorkspaces", workspaces); + alg->setProperty("Efficiencies", efficiencies); + if (!isDefault(Prop::FLIPPERS)) { + alg->setPropertyValue("Flippers", getPropertyValue(Prop::FLIPPERS)); } + auto out = getPropertyValue(Prop::OUTPUT_WORKSPACES); + alg->setPropertyValue("OutputWorkspace", out); + alg->execute(); + API::WorkspaceGroup_sptr outWS = alg->getProperty("OutputWorkspace"); + setProperty(Prop::OUTPUT_WORKSPACES, outWS); } -/** - * Check that all workspaces and efficicencies have the same X data. - * @param inputs a set of workspaces to check - * @param efficiencies efficiencies to check - */ -void PolarizationEfficiencyCor::checkConsistentX( - const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { - // Compare everything to F1 efficiency. - const auto &F1x = efficiencies.F1->x(); - // A local helper function to check a HistogramX against F1. - auto checkX = - [&F1x](const HistogramData::HistogramX &x, const std::string &tag) { - if (x.size() != F1x.size()) { - throw std::runtime_error( - "Mismatch of histogram lengths between F1 and " + tag + '.'); - } - for (size_t i = 0; i != x.size(); ++i) { - if (x[i] != F1x[i]) { - throw std::runtime_error("Mismatch of X data between F1 and " + - tag + '.'); - } - } - }; - const auto &F2x = efficiencies.F2->x(); - checkX(F2x, "F2"); - const auto &P1x = efficiencies.P1->x(); - checkX(P1x, "P1"); - const auto &P2x = efficiencies.P2->x(); - checkX(P2x, "P2"); - // A local helper function to check an input workspace against F1. - auto checkWS = - [&checkX](const API::MatrixWorkspace_sptr &ws, const std::string &tag) { - const auto nHist = ws->getNumberHistograms(); - for (size_t i = 0; i != nHist; ++i) { - checkX(ws->x(i), tag); - } - }; - if (inputs.mmWS) { - checkWS(inputs.mmWS, Flippers::OffOff); - } - if (inputs.mpWS) { - checkWS(inputs.mpWS, Flippers::OffOn); - } - if (inputs.pmWS) { - checkWS(inputs.pmWS, Flippers::OnOff); - } - if (inputs.ppWS) { - checkWS(inputs.ppWS, Flippers::OnOn); +//---------------------------------------------------------------------------------------------- +void PolarizationEfficiencyCor::execFredrikze() { + checkFredrikzeProperties(); + WorkspaceGroup_sptr group = getWorkspaceGroup(); + MatrixWorkspace_sptr efficiencies = getEfficiencies(); + auto alg = createChildAlgorithm("PolarizationCorrectionFredrikze"); + alg->initialize(); + alg->setProperty("InputWorkspace", group); + alg->setProperty("Efficiencies", efficiencies); + if (!isDefault(Prop::POLARIZATION_ANALYSIS)) { + alg->setPropertyValue("PolarizationAnalysis", + getPropertyValue(Prop::POLARIZATION_ANALYSIS)); } + alg->setPropertyValue("OutputWorkspace", + getPropertyValue(Prop::OUTPUT_WORKSPACES)); + alg->execute(); + API::WorkspaceGroup_sptr outWS = alg->getProperty("OutputWorkspace"); + setProperty(Prop::OUTPUT_WORKSPACES, outWS); } -/** - * Make a workspace group out of the given set of workspaces. - * The workspaces will be published in the ADS, their names appended by - * appropriate suffices. - * @param outputs a set of workspaces to group - * @return a group workspace +//---------------------------------------------------------------------------------------------- +/** Check that the inputs workspaces are set. */ -API::WorkspaceGroup_sptr -PolarizationEfficiencyCor::groupOutput(const WorkspaceMap &outputs) { - const std::string outWSName = getProperty(Prop::OUTPUT_WS); - std::vector<std::string> names; - if (outputs.mmWS) { - names.emplace_back(outWSName + "_--"); - API::AnalysisDataService::Instance().addOrReplace(names.back(), - outputs.mmWS); - } - if (outputs.mpWS) { - names.emplace_back(outWSName + "_-+"); - API::AnalysisDataService::Instance().addOrReplace(names.back(), - outputs.mpWS); +void PolarizationEfficiencyCor::checkWorkspaces() const { + if (isDefault(Prop::INPUT_WORKSPACES) && + isDefault(Prop::INPUT_WORKSPACE_GROUP)) { + throw std::invalid_argument("Input workspaces are missing. Either a " + "workspace group or a list of workspace names " + "must be given."); } - if (outputs.pmWS) { - names.emplace_back(outWSName + "_+-"); - API::AnalysisDataService::Instance().addOrReplace(names.back(), - outputs.pmWS); + if (!isDefault(Prop::INPUT_WORKSPACES) && + !isDefault(Prop::INPUT_WORKSPACE_GROUP)) { + throw std::invalid_argument("Input workspaces must be given either as a " + "workspace group or a list of names."); } - if (outputs.ppWS) { - names.emplace_back(outWSName + "_++"); - API::AnalysisDataService::Instance().addOrReplace(names.back(), - outputs.ppWS); - } - auto group = createChildAlgorithm("GroupWorkspaces"); - group->initialize(); - group->setProperty("InputWorkspaces", names); - group->setProperty("OutputWorkspace", outWSName); - group->execute(); - API::WorkspaceGroup_sptr outWS = group->getProperty("OutputWorkspace"); - return outWS; } -/** - * Make a convenience access object to the efficiency factors. - * @return an EfficiencyMap object +//---------------------------------------------------------------------------------------------- +/** Check that the inputs for the Wildes are correct and consistent. */ -PolarizationEfficiencyCor::EfficiencyMap -PolarizationEfficiencyCor::efficiencyFactors() { - EfficiencyMap e; - API::MatrixWorkspace_const_sptr factorWS = getProperty(Prop::EFFICIENCIES); - const auto &vertAxis = factorWS->getAxis(1); - for (size_t i = 0; i != vertAxis->length(); ++i) { - const auto label = vertAxis->label(i); - if (label == "P1") { - e.P1 = &factorWS->getSpectrum(i); - } else if (label == "P2") { - e.P2 = &factorWS->getSpectrum(i); - } else if (label == "F1") { - e.F1 = &factorWS->getSpectrum(i); - } else if (label == "F2") { - e.F2 = &factorWS->getSpectrum(i); - } - // Ignore other histograms such as 'Phi' in ILL's efficiency ws. +void PolarizationEfficiencyCor::checkWildesProperties() const { + checkWorkspaces(); + + if (!isDefault(Prop::POLARIZATION_ANALYSIS)) { + throw std::invalid_argument( + "Property PolarizationAnalysis cannot be used with the Wildes method."); } - return e; } -/** - * Correct a direct beam measurement for non-ideal instrument effects. - * Only the non-analyzer, polarizer flipper off case is considered here. - * @param inputs a set of workspaces to correct - * @param efficiencies a set of efficiency factors - * @return set of corrected workspaces +//---------------------------------------------------------------------------------------------- +/** Check that the inputs for the Fredrikze method are correct and consistent. */ -PolarizationEfficiencyCor::WorkspaceMap -PolarizationEfficiencyCor::directBeamCorrections( - const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { - using namespace boost::math; - checkInputExists(inputs.ppWS, Flippers::Off); - WorkspaceMap outputs; - outputs.ppWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); - const size_t nHisto = inputs.ppWS->getNumberHistograms(); - for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { - const auto &ppY = inputs.ppWS->y(wsIndex); - const auto &ppE = inputs.ppWS->e(wsIndex); - auto &ppYOut = outputs.ppWS->mutableY(wsIndex); - auto &ppEOut = outputs.ppWS->mutableE(wsIndex); - for (size_t binIndex = 0; binIndex < ppY.size(); ++binIndex) { - const auto P1 = efficiencies.P1->y()[binIndex]; - const auto P2 = efficiencies.P2->y()[binIndex]; - const double f = 1. - P1 - P2 + 2. * P1 * P2; - ppYOut[binIndex] = ppY[binIndex] / f; - const auto P1E = efficiencies.P1->e()[binIndex]; - const auto P2E = efficiencies.P2->e()[binIndex]; - const auto e1 = pow<2>(P1E * (2. * P1 - 1.) / pow<2>(f) * ppY[binIndex]); - const auto e2 = pow<2>(P2E * (2. * P2 - 1.) / pow<2>(f) * ppY[binIndex]); - const auto e3 = pow<2>(ppE[binIndex] / f); - const auto errorSum = std::sqrt(e1 + e2 + e3); - ppEOut[binIndex] = errorSum; - } +void PolarizationEfficiencyCor::checkFredrikzeProperties() const { + checkWorkspaces(); + + if (!isDefault(Prop::FLIPPERS)) { + throw std::invalid_argument( + "Property Flippers cannot be used with the Fredrikze method."); } - return outputs; } -/** - * Correct for non-ideal instrument effects. - * Deals with the case when the data was taken without the analyzer: - * only the polarizer flipper is used. - * @param inputs a set of workspaces to correct - * @param efficiencies a set of efficiency factors - * @return a set of corrected workspaces +//---------------------------------------------------------------------------------------------- +/** Get the input workspaces as a list of names. */ -PolarizationEfficiencyCor::WorkspaceMap -PolarizationEfficiencyCor::analyzerlessCorrections( - const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { - using namespace boost::math; - checkInputExists(inputs.mmWS, Flippers::On); - checkInputExists(inputs.ppWS, Flippers::Off); - WorkspaceMap outputs; - outputs.mmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mmWS); - outputs.ppWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); - const size_t nHisto = inputs.mmWS->getNumberHistograms(); - for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { - const auto &mmY = inputs.mmWS->y(wsIndex); - const auto &mmE = inputs.mmWS->e(wsIndex); - const auto &ppY = inputs.ppWS->y(wsIndex); - const auto &ppE = inputs.ppWS->e(wsIndex); - auto &mmYOut = outputs.mmWS->mutableY(wsIndex); - auto &mmEOut = outputs.mmWS->mutableE(wsIndex); - auto &ppYOut = outputs.ppWS->mutableY(wsIndex); - auto &ppEOut = outputs.ppWS->mutableE(wsIndex); - for (size_t binIndex = 0; binIndex < mmY.size(); ++binIndex) { - const auto F1 = efficiencies.F1->y()[binIndex]; - const auto P1 = efficiencies.P1->y()[binIndex]; - Eigen::Matrix2d F1m; - F1m << 1., 0., (F1 - 1.) / F1, 1. / F1; - const double divisor = (2. * P1 - 1.); - const double diag = (P1 - 1.) / divisor; - const double off = P1 / divisor; - Eigen::Matrix2d P1m; - P1m << diag, off, off, diag; - const Eigen::Vector2d intensities(ppY[binIndex], mmY[binIndex]); - const auto PFProduct = P1m * F1m; - const auto corrected = PFProduct * intensities; - ppYOut[binIndex] = corrected[0]; - mmYOut[binIndex] = corrected[1]; - const auto F1E = efficiencies.F1->e()[binIndex]; - const auto P1E = efficiencies.P1->e()[binIndex]; - const auto elemE1 = -1. / pow<2>(F1) * F1E; - Eigen::Matrix2d F1Em; - F1Em << 0., 0., -elemE1, elemE1; - const auto elemE2 = 1. / pow<2>(divisor) * P1E; - Eigen::Matrix2d P1Em; - P1Em << elemE2, -elemE2, -elemE2, elemE2; - const Eigen::Vector2d errors(ppE[binIndex], mmE[binIndex]); - const auto e1 = (P1Em * F1m * intensities).array(); - const auto e2 = (P1m * F1Em * intensities).array(); - const auto sqPFProduct = (PFProduct.array() * PFProduct.array()).matrix(); - const auto sqErrors = (errors.array() * errors.array()).matrix(); - const auto e3 = (sqPFProduct * sqErrors).array(); - const auto errorSum = (e1 * e1 + e2 * e2 + e3).sqrt(); - ppEOut[binIndex] = errorSum[0]; - mmEOut[binIndex] = errorSum[1]; +std::vector<std::string> +PolarizationEfficiencyCor::getWorkspaceNameList() const { + std::vector<std::string> names; + if (!isDefault(Prop::INPUT_WORKSPACES)) { + names = getProperty(Prop::INPUT_WORKSPACES); + } else { + WorkspaceGroup_sptr group = getProperty(Prop::INPUT_WORKSPACE_GROUP); + auto const n = group->size(); + for (size_t i = 0; i < n; ++i) { + auto ws = group->getItem(i); + auto const name = ws->getName(); + if (name.empty()) { + throw std::invalid_argument( + "Workspace from the input workspace group is not stored in the " + "Analysis Data Service which is required by the Wildes method."); + } + names.push_back(name); } } - return outputs; + return names; } -/** - * Correct for non-ideal instrument effects. - * Only 00 and 11 flipper configurations need to be provided; - * the missing 01 and 10 data is solved from the assumption that - * in the corrected data, R01 = R10 = 0. - * @param inputs a set of workspaces to correct - * @param efficiencies a set of efficiency factors - * @return a set of corrected workspaces - */ -PolarizationEfficiencyCor::WorkspaceMap -PolarizationEfficiencyCor::twoInputCorrections( - const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { - using namespace boost::math; - checkInputExists(inputs.mmWS, Flippers::OnOn); - checkInputExists(inputs.ppWS, Flippers::OffOff); - WorkspaceMap fullInputs = inputs; - fullInputs.mpWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mmWS); - fullInputs.pmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); - twoInputsSolve01And10(fullInputs, inputs, efficiencies); - return fullCorrections(fullInputs, efficiencies); -} - -/** - * Correct for non-ideal instrument effects. - * Needs the 00 and 11 flipper configurations as well as either 01 or 10. - * The missing intensity (01 or 10) is solved from the assumption - * that the corrected R01 = R10. - * @param inputs a set of workspaces to correct - * @param efficiencies a set of efficiency factors - * @return a set of corrected workspaces +//---------------------------------------------------------------------------------------------- +/** Get the input workspaces as a workspace group. */ -PolarizationEfficiencyCor::WorkspaceMap -PolarizationEfficiencyCor::threeInputCorrections( - const WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { - WorkspaceMap fullInputs = inputs; - checkInputExists(inputs.mmWS, Flippers::OnOn); - checkInputExists(inputs.ppWS, Flippers::OffOff); - if (!inputs.mpWS) { - checkInputExists(inputs.pmWS, Flippers::OffOn); - threeInputsSolve10(fullInputs, efficiencies); +API::WorkspaceGroup_sptr PolarizationEfficiencyCor::getWorkspaceGroup() const { + WorkspaceGroup_sptr group; + if (!isDefault(Prop::INPUT_WORKSPACE_GROUP)) { + group = getProperty(Prop::INPUT_WORKSPACE_GROUP); } else { - checkInputExists(inputs.mpWS, Flippers::OnOff); - threeInputsSolve01(fullInputs, efficiencies); + throw std::invalid_argument( + "Input workspaces are required to be in a workspace group."); } - return fullCorrections(fullInputs, efficiencies); + return group; } -/** - * Correct for non-ideal instrument effects. - * Perform full polarization corrections. All flipper configurations - * (00, 01, 10 and 11) are needed for this. - * @param inputs a set of workspaces to correct - * @param efficiencies a set of efficiency factors - * @return a set of corrected workspaces - */ -PolarizationEfficiencyCor::WorkspaceMap -PolarizationEfficiencyCor::fullCorrections(const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies) { - using namespace boost::math; - checkInputExists(inputs.mmWS, Flippers::OnOn); - checkInputExists(inputs.mpWS, Flippers::OnOff); - checkInputExists(inputs.pmWS, Flippers::OffOn); - checkInputExists(inputs.ppWS, Flippers::OffOff); - WorkspaceMap outputs; - outputs.mmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mmWS); - outputs.mpWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.mpWS); - outputs.pmWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.pmWS); - outputs.ppWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.ppWS); - const auto F1 = efficiencies.F1->y(); - const auto F1E = efficiencies.F1->e(); - const auto F2 = efficiencies.F2->y(); - const auto F2E = efficiencies.F2->e(); - const auto P1 = efficiencies.P1->y(); - const auto P1E = efficiencies.P1->e(); - const auto P2 = efficiencies.P2->y(); - const auto P2E = efficiencies.P2->e(); - const size_t nHisto = inputs.mmWS->getNumberHistograms(); - for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { - const auto &mmY = inputs.mmWS->y(wsIndex); - const auto &mmE = inputs.mmWS->e(wsIndex); - const auto &mpY = inputs.mpWS->y(wsIndex); - const auto &mpE = inputs.mpWS->e(wsIndex); - const auto &pmY = inputs.pmWS->y(wsIndex); - const auto &pmE = inputs.pmWS->e(wsIndex); - const auto &ppY = inputs.ppWS->y(wsIndex); - const auto &ppE = inputs.ppWS->e(wsIndex); - auto &mmYOut = outputs.mmWS->mutableY(wsIndex); - auto &mmEOut = outputs.mmWS->mutableE(wsIndex); - auto &mpYOut = outputs.mpWS->mutableY(wsIndex); - auto &mpEOut = outputs.mpWS->mutableE(wsIndex); - auto &pmYOut = outputs.pmWS->mutableY(wsIndex); - auto &pmEOut = outputs.pmWS->mutableE(wsIndex); - auto &ppYOut = outputs.ppWS->mutableY(wsIndex); - auto &ppEOut = outputs.ppWS->mutableE(wsIndex); - for (size_t binIndex = 0; binIndex < mmY.size(); ++binIndex) { - Eigen::Vector4d corrected; - Eigen::Vector4d errors; - fourInputsCorrectedAndErrors(corrected, errors, ppY[binIndex], - ppE[binIndex], pmY[binIndex], pmE[binIndex], - mpY[binIndex], mpE[binIndex], mmY[binIndex], - mmE[binIndex], F1[binIndex], F1E[binIndex], - F2[binIndex], F2E[binIndex], P1[binIndex], - P1E[binIndex], P2[binIndex], P2E[binIndex]); - ppYOut[binIndex] = corrected[0]; - pmYOut[binIndex] = corrected[1]; - mpYOut[binIndex] = corrected[2]; - mmYOut[binIndex] = corrected[3]; - ppEOut[binIndex] = errors[0]; - pmEOut[binIndex] = errors[1]; - mpEOut[binIndex] = errors[2]; - mmEOut[binIndex] = errors[3]; - } +//---------------------------------------------------------------------------------------------- +/// Check if efficiencies workspace needs interpolation. Use inWS as for +/// comparison. +bool PolarizationEfficiencyCor::needInterpolation( + MatrixWorkspace const &efficiencies, MatrixWorkspace const &inWS) const { + + if (!efficiencies.isHistogramData()) + return true; + if (efficiencies.blocksize() != inWS.blocksize()) + return true; + + auto const &x = inWS.x(0); + for (size_t i = 0; i < efficiencies.getNumberHistograms(); ++i) { + if (efficiencies.x(i).rawData() != x.rawData()) + return true; } - return outputs; + return false; } -/** - * Make a set of workspaces to correct from input properties. - * @param flippers a vector of flipper configurations - * @return a set of workspaces to correct - */ -PolarizationEfficiencyCor::WorkspaceMap -PolarizationEfficiencyCor::mapInputsToDirections( - const std::vector<std::string> &flippers) { - const std::vector<std::string> inputNames = getProperty(Prop::INPUT_WS); - WorkspaceMap inputs; - for (size_t i = 0; i < flippers.size(); ++i) { - auto ws = - (API::AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>( - inputNames[i])); - if (!ws) { - throw std::runtime_error( - "One of the input workspaces doesn't seem to be a MatrixWorkspace."); - } - const auto &f = flippers[i]; - if (f == Flippers::OnOn || f == Flippers::On) { - inputs.mmWS = ws; - } else if (f == Flippers::OnOff) { - inputs.mpWS = ws; - } else if (f == Flippers::OffOn) { - inputs.pmWS = ws; - } else if (f == Flippers::OffOff || f == Flippers::Off) { - inputs.ppWS = ws; - } else { - throw std::runtime_error(std::string{"Unknown entry in "} + - Prop::FLIPPERS); - } +//---------------------------------------------------------------------------------------------- +/// Convert the efficiencies to histogram +MatrixWorkspace_sptr PolarizationEfficiencyCor::convertToHistogram( + API::MatrixWorkspace_sptr efficiencies) { + if (efficiencies->isHistogramData()) { + return efficiencies; } - return inputs; + auto alg = createChildAlgorithm("ConvertToHistogram"); + alg->initialize(); + alg->setProperty("InputWorkspace", efficiencies); + alg->setProperty("OutputWorkspace", "dummy"); + alg->execute(); + MatrixWorkspace_sptr result = alg->getProperty("OutputWorkspace"); + return result; } -/** - * Solve in-place the 01 flipper configuration from the assumption that - * for the corrected intensities, R01 = R10. - * @param inputs a set of input workspaces - * @param efficiencies a set of efficiency factors - */ -void PolarizationEfficiencyCor::threeInputsSolve01( - WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { - using namespace Mantid::DataObjects; - inputs.pmWS = create<Workspace2D>(*inputs.mpWS); - const auto &F1 = efficiencies.F1->y(); - const auto &F2 = efficiencies.F2->y(); - const auto &P1 = efficiencies.P1->y(); - const auto &P2 = efficiencies.P2->y(); - const auto nHisto = inputs.pmWS->getNumberHistograms(); - for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { - const auto &I00 = inputs.ppWS->y(wsIndex); - auto &I01 = inputs.pmWS->mutableY(wsIndex); - const auto &I10 = inputs.mpWS->y(wsIndex); - const auto &I11 = inputs.mmWS->y(wsIndex); - for (size_t binIndex = 0; binIndex != I00.size(); ++binIndex) { - const auto f1 = F1[binIndex]; - const auto f2 = F2[binIndex]; - const auto p1 = P1[binIndex]; - const auto p2 = P2[binIndex]; - const auto i00 = I00[binIndex]; - const auto i10 = I10[binIndex]; - const auto i11 = I11[binIndex]; - I01[binIndex] = - (f1 * i00 * (-1. + 2. * p1) - (i00 - i10 + i11) * (p1 - p2) - - f2 * (i00 - i10) * (-1. + 2. * p2)) / - (-p1 + f1 * (-1. + 2. * p1) + p2); - // The errors are left to zero. - } - } +//---------------------------------------------------------------------------------------------- +/// Convert the efficiencies to histogram +MatrixWorkspace_sptr +PolarizationEfficiencyCor::interpolate(MatrixWorkspace_sptr efficiencies, + MatrixWorkspace_sptr inWS) { + + efficiencies->setDistribution(true); + auto alg = createChildAlgorithm("RebinToWorkspace"); + alg->initialize(); + alg->setProperty("WorkspaceToRebin", efficiencies); + alg->setProperty("WorkspaceToMatch", inWS); + alg->setProperty("OutputWorkspace", "dummy"); + alg->execute(); + MatrixWorkspace_sptr result = alg->getProperty("OutputWorkspace"); + return result; } -/** - * Solve in-place the 10 flipper configuration from the assumption that - * for the corrected intensities R01 = R10. - * @param inputs a set of input workspaces - * @param efficiencies a set of efficiency factors +//---------------------------------------------------------------------------------------------- +/** Prepare and return the efficiencies. */ -void PolarizationEfficiencyCor::threeInputsSolve10( - WorkspaceMap &inputs, const EfficiencyMap &efficiencies) { - inputs.mpWS = DataObjects::create<DataObjects::Workspace2D>(*inputs.pmWS); - const auto &F1 = efficiencies.F1->y(); - const auto &F2 = efficiencies.F2->y(); - const auto &P1 = efficiencies.P1->y(); - const auto &P2 = efficiencies.P2->y(); - const auto nHisto = inputs.mpWS->getNumberHistograms(); - for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { - const auto &I00 = inputs.ppWS->y(wsIndex); - const auto &I01 = inputs.pmWS->y(wsIndex); - auto &I10 = inputs.mpWS->mutableY(wsIndex); - const auto &I11 = inputs.mmWS->y(wsIndex); - for (size_t binIndex = 0; binIndex != I00.size(); ++binIndex) { - const auto f1 = F1[binIndex]; - const auto f2 = F2[binIndex]; - const auto p1 = P1[binIndex]; - const auto p2 = P2[binIndex]; - const auto i00 = I00[binIndex]; - const auto i01 = I01[binIndex]; - const auto i11 = I11[binIndex]; - I10[binIndex] = - (-f1 * (i00 - i01) * (-1. + 2. * p1) + (i00 - i01 + i11) * (p1 - p2) + - f2 * i00 * (-1. + 2. * p2)) / - (p1 - p2 + f2 * (-1. + 2. * p2)); - // The errors are left to zero. - } +API::MatrixWorkspace_sptr PolarizationEfficiencyCor::getEfficiencies() { + MatrixWorkspace_sptr inWS; + if (!isDefault(Prop::INPUT_WORKSPACES)) { + std::vector<std::string> const names = getProperty(Prop::INPUT_WORKSPACES); + inWS = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( + names.front()); + } else { + WorkspaceGroup_sptr group = getProperty(Prop::INPUT_WORKSPACE_GROUP); + inWS = boost::dynamic_pointer_cast<MatrixWorkspace>(group->getItem(0)); } -} + MatrixWorkspace_sptr efficiencies = getProperty(Prop::EFFICIENCIES); -/** - * Solve in-place the 01 and 10 flipper configurations from the assumption that - * for the corrected intensities R01 = R10 = 0. - * @param fullInputs a set of output workspaces - * @param inputs a set of input workspaces - * @param efficiencies a set of efficiency factors - */ -void PolarizationEfficiencyCor::twoInputsSolve01And10( - WorkspaceMap &fullInputs, const WorkspaceMap &inputs, - const EfficiencyMap &efficiencies) { - using namespace boost::math; - const auto &F1 = efficiencies.F1->y(); - const auto &F1E = efficiencies.F1->e(); - const auto &F2 = efficiencies.F2->y(); - const auto &F2E = efficiencies.F2->e(); - const auto &P1 = efficiencies.P1->y(); - const auto &P1E = efficiencies.P1->e(); - const auto &P2 = efficiencies.P2->y(); - const auto &P2E = efficiencies.P2->e(); - const auto nHisto = inputs.mmWS->getNumberHistograms(); - for (size_t wsIndex = 0; wsIndex != nHisto; ++wsIndex) { - const auto &I00 = inputs.ppWS->y(wsIndex); - const auto &E00 = inputs.ppWS->e(wsIndex); - const auto &I11 = inputs.mmWS->y(wsIndex); - const auto &E11 = inputs.mmWS->e(wsIndex); - auto &I01 = fullInputs.pmWS->mutableY(wsIndex); - auto &E01 = fullInputs.pmWS->mutableE(wsIndex); - auto &I10 = fullInputs.mpWS->mutableY(wsIndex); - auto &E10 = fullInputs.mpWS->mutableE(wsIndex); - for (size_t binIndex = 0; binIndex != I00.size(); ++binIndex) { - const auto i00 = I00[binIndex]; - const auto i11 = I11[binIndex]; - const auto f1 = F1[binIndex]; - const auto f2 = F2[binIndex]; - const auto p1 = P1[binIndex]; - const auto p2 = P2[binIndex]; - const auto a = -1. + p1 + 2. * p2 - 2. * p1 * p2; - const auto b = -1. + 2. * p1; - const auto c = -1. + 2. * p2; - const auto d = -1. + p2; - // Case: 01 - const auto divisor = f2 * p1 * a + f1 * b * (-d * p2 + f2 * (p1 + d) * c); - I01[binIndex] = - (f2 * i11 * p1 * a - - f1 * i00 * b * (-f2 * pow<2>(c) + pow<2>(f2 * c) + d * p2)) / - divisor; - E01[binIndex] = twoInputsErrorEstimate01( - i00, E00[binIndex], i11, E11[binIndex], p1, P1E[binIndex], p2, - P2E[binIndex], f1, F1E[binIndex], f2, F2E[binIndex]); - // Case: 10 - I10[binIndex] = - (-pow<2>(f1) * f2 * i00 * pow<2>(b) * c + f2 * i00 * p1 * a + - f1 * b * (-i11 * d * p2 + f2 * i00 * b * c)) / - divisor; - E10[binIndex] = twoInputsErrorEstimate10( - i00, E00[binIndex], i11, E11[binIndex], p1, P1E[binIndex], p2, - P2E[binIndex], f1, F1E[binIndex], f2, F2E[binIndex]); - } + if (!needInterpolation(*efficiencies, *inWS)) { + return efficiencies; } + + efficiencies = convertToHistogram(efficiencies); + efficiencies = interpolate(efficiencies, inWS); + + return efficiencies; } + } // namespace Algorithms } // namespace Mantid diff --git a/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp b/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp index f30e521cbc958668118733bc46f3a373e63d6498..79d0390e81d129d21d2681a5b4cdf2f23ae19655 100644 --- a/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp +++ b/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp @@ -6,8 +6,8 @@ #include "MantidKernel/CompositeValidator.h" #include "MantidKernel/EnabledWhenProperty.h" #include "MantidKernel/ListValidator.h" -#include "MantidKernel/make_unique.h" #include "MantidKernel/MandatoryValidator.h" +#include "MantidKernel/make_unique.h" namespace Mantid { namespace Algorithms { @@ -42,9 +42,9 @@ const std::string ReflectometryReductionOneAuto2::summary() const { } /** Validate transmission runs -* -* @return :: result of the validation as a map -*/ + * + * @return :: result of the validation as a map + */ std::map<std::string, std::string> ReflectometryReductionOneAuto2::validateInputs() { @@ -104,7 +104,7 @@ ReflectometryReductionOneAuto2::validateInputs() { } /** Initialize the algorithm's properties. -*/ + */ void ReflectometryReductionOneAuto2::init() { // Input ws @@ -190,30 +190,30 @@ void ReflectometryReductionOneAuto2::init() { boost::make_shared<StringListValidator>(propOptions), "Polarization analysis mode."); declareProperty( - Kernel::make_unique<ArrayProperty<double>>("CPp", Direction::Input), + Kernel::make_unique<ArrayProperty<double>>("Pp", Direction::Input), "Effective polarizing power of the polarizing system. " "Expressed as a ratio 0 < Pp < 1"); declareProperty( - Kernel::make_unique<ArrayProperty<double>>("CAp", Direction::Input), + Kernel::make_unique<ArrayProperty<double>>("Ap", Direction::Input), "Effective polarizing power of the analyzing system. " "Expressed as a ratio 0 < Ap < 1"); declareProperty( - Kernel::make_unique<ArrayProperty<double>>("CRho", Direction::Input), + Kernel::make_unique<ArrayProperty<double>>("Rho", Direction::Input), "Ratio of efficiencies of polarizer spin-down to polarizer " "spin-up. This is characteristic of the polarizer flipper. " "Values are constants for each term in a polynomial " "expression."); declareProperty( - Kernel::make_unique<ArrayProperty<double>>("CAlpha", Direction::Input), + Kernel::make_unique<ArrayProperty<double>>("Alpha", Direction::Input), "Ratio of efficiencies of analyzer spin-down to analyzer " "spin-up. This is characteristic of the analyzer flipper. " "Values are factors for each term in a polynomial " "expression."); setPropertyGroup("PolarizationAnalysis", "Polarization Corrections"); - setPropertyGroup("CPp", "Polarization Corrections"); - setPropertyGroup("CAp", "Polarization Corrections"); - setPropertyGroup("CRho", "Polarization Corrections"); - setPropertyGroup("CAlpha", "Polarization Corrections"); + setPropertyGroup("Pp", "Polarization Corrections"); + setPropertyGroup("Ap", "Polarization Corrections"); + setPropertyGroup("Rho", "Polarization Corrections"); + setPropertyGroup("Alpha", "Polarization Corrections"); // Init properties for diagnostics initDebugProperties(); @@ -235,7 +235,7 @@ void ReflectometryReductionOneAuto2::init() { } /** Execute the algorithm. -*/ + */ void ReflectometryReductionOneAuto2::exec() { MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace"); @@ -325,14 +325,14 @@ void ReflectometryReductionOneAuto2::exec() { } /** Returns the detectors of interest, specified via processing instructions. -* Note that this returns the names of the parent detectors of the first and -* last spectrum indices in the processing instructions. It is assumed that all -* the interim detectors have the same parent. -* -* @param instructions :: processing instructions defining detectors of interest -* @param inputWS :: the input workspace -* @return :: the names of the detectors of interest -*/ + * Note that this returns the names of the parent detectors of the first and + * last spectrum indices in the processing instructions. It is assumed that all + * the interim detectors have the same parent. + * + * @param instructions :: processing instructions defining detectors of interest + * @param inputWS :: the input workspace + * @return :: the names of the detectors of interest + */ std::vector<std::string> ReflectometryReductionOneAuto2::getDetectorNames( const std::string &instructions, MatrixWorkspace_sptr inputWS) { @@ -365,14 +365,14 @@ std::vector<std::string> ReflectometryReductionOneAuto2::getDetectorNames( } /** Correct an instrument component by shifting it vertically or -* rotating it around the sample. -* -* @param instructions :: processing instructions defining the detectors of -* interest -* @param inputWS :: the input workspace -* @param twoTheta :: the angle to move detectors to -* @return :: the corrected workspace -*/ + * rotating it around the sample. + * + * @param instructions :: processing instructions defining the detectors of + * interest + * @param inputWS :: the input workspace + * @param twoTheta :: the angle to move detectors to + * @return :: the corrected workspace + */ MatrixWorkspace_sptr ReflectometryReductionOneAuto2::correctDetectorPositions( const std::string &instructions, MatrixWorkspace_sptr inputWS, const double twoTheta) { @@ -407,13 +407,13 @@ MatrixWorkspace_sptr ReflectometryReductionOneAuto2::correctDetectorPositions( } /** Calculate the theta value of the detector of interest specified via -* processing instructions -* -* @param instructions :: processing instructions defining the detectors of -* interest -* @param inputWS :: the input workspace -* @return :: the angle of the detector (only the first detector is considered) -*/ + * processing instructions + * + * @param instructions :: processing instructions defining the detectors of + * interest + * @param inputWS :: the input workspace + * @return :: the angle of the detector (only the first detector is considered) + */ double ReflectometryReductionOneAuto2::calculateTheta(const std::string &instructions, MatrixWorkspace_sptr inputWS) { @@ -438,10 +438,10 @@ ReflectometryReductionOneAuto2::calculateTheta(const std::string &instructions, } /** Set algorithmic correction properties -* -* @param alg :: ReflectometryReductionOne algorithm -* @param instrument :: The instrument attached to the workspace -*/ + * + * @param alg :: ReflectometryReductionOne algorithm + * @param instrument :: The instrument attached to the workspace + */ void ReflectometryReductionOneAuto2::populateAlgorithmicCorrectionProperties( IAlgorithm_sptr alg, Instrument_const_sptr instrument) { @@ -507,12 +507,12 @@ void ReflectometryReductionOneAuto2::populateAlgorithmicCorrectionProperties( } /** Rebin and scale a workspace in Q. -* -* @param inputWS :: the workspace in Q -* @param theta :: the angle of this run -* @param params :: [output] rebin parameters -* @return :: the output workspace -*/ + * + * @param inputWS :: the workspace in Q + * @param theta :: the angle of this run + * @param params :: [output] rebin parameters + * @return :: the output workspace + */ MatrixWorkspace_sptr ReflectometryReductionOneAuto2::rebinAndScale(MatrixWorkspace_sptr inputWS, const double theta, @@ -585,7 +585,7 @@ ReflectometryReductionOneAuto2::rebinAndScale(MatrixWorkspace_sptr inputWS, } /** Check if input workspace is a group -*/ + */ bool ReflectometryReductionOneAuto2::checkGroups() { const std::string wsName = getPropertyValue("InputWorkspace"); @@ -611,7 +611,7 @@ bool ReflectometryReductionOneAuto2::checkGroups() { * items in the transmission group will be summed to produce a matrix workspace * that will be applied to each of the items in the input workspace group. See * documentation of this algorithm for more details. -*/ + */ bool ReflectometryReductionOneAuto2::processGroups() { // this algorithm effectively behaves as MultiPeriodGroupAlgorithm m_usingBaseProcessGroups = true; @@ -744,26 +744,34 @@ bool ReflectometryReductionOneAuto2::processGroups() { return true; } - if (!group->isMultiperiod()) { - g_log.warning("Polarization corrections can only be performed on " - "multiperiod workspaces."); - setPropertyValue("OutputWorkspace", outputIvsQ); - setPropertyValue("OutputWorkspaceBinned", outputIvsQBinned); - setPropertyValue("OutputWorkspaceWavelength", outputIvsLam); - return true; + auto groupIvsLam = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(outputIvsLam); + auto effAlg = createChildAlgorithm("CreatePolarizationEfficiencies"); + effAlg->setProperty("InputWorkspace", groupIvsLam->getItem(0)); + if (!isDefault("Pp")) { + effAlg->setProperty("Pp", getPropertyValue("Pp")); + } + if (!isDefault("Rho")) { + effAlg->setProperty("Rho", getPropertyValue("Rho")); + } + if (!isDefault("Ap")) { + effAlg->setProperty("Ap", getPropertyValue("Ap")); + } + if (!isDefault("Alpha")) { + effAlg->setProperty("Alpha", getPropertyValue("Alpha")); } + effAlg->execute(); + MatrixWorkspace_sptr efficiencies = effAlg->getProperty("OutputWorkspace"); - Algorithm_sptr polAlg = createChildAlgorithm("PolarizationCorrection"); + Algorithm_sptr polAlg = + createChildAlgorithm("PolarizationCorrectionFredrikze"); polAlg->setChild(false); polAlg->setRethrows(true); polAlg->setProperty("InputWorkspace", outputIvsLam); polAlg->setProperty("OutputWorkspace", outputIvsLam); polAlg->setProperty("PolarizationAnalysis", getPropertyValue("PolarizationAnalysis")); - polAlg->setProperty("CPp", getPropertyValue("CPp")); - polAlg->setProperty("CRho", getPropertyValue("CRho")); - polAlg->setProperty("CAp", getPropertyValue("CAp")); - polAlg->setProperty("CAlpha", getPropertyValue("CAlpha")); + polAlg->setProperty("Efficiencies", efficiencies); polAlg->execute(); // Now we've overwritten the IvsLam workspaces, we'll need to recalculate @@ -793,10 +801,10 @@ bool ReflectometryReductionOneAuto2::processGroups() { } /** -* Sum transmission workspaces that belong to a workspace group -* @param transGroup : The transmission group containing the transmission runs -* @return :: A workspace pointer containing the sum of transmission workspaces -*/ + * Sum transmission workspaces that belong to a workspace group + * @param transGroup : The transmission group containing the transmission runs + * @return :: A workspace pointer containing the sum of transmission workspaces + */ MatrixWorkspace_sptr ReflectometryReductionOneAuto2::sumTransmissionWorkspaces( WorkspaceGroup_sptr &transGroup) { diff --git a/Framework/Algorithms/test/PolarizationCorrectionTest.h b/Framework/Algorithms/test/PolarizationCorrectionFredrikzeTest.h similarity index 51% rename from Framework/Algorithms/test/PolarizationCorrectionTest.h rename to Framework/Algorithms/test/PolarizationCorrectionFredrikzeTest.h index 921b60eca4059822c7abb40815c407220f47ab63..0e3d0d69fe3e1dee18d09aa3cb963289b6dc11f6 100644 --- a/Framework/Algorithms/test/PolarizationCorrectionTest.h +++ b/Framework/Algorithms/test/PolarizationCorrectionFredrikzeTest.h @@ -1,58 +1,68 @@ -#ifndef MANTID_ALGORITHMS_POLARIZATIONCORRECTION_TEST_H_ -#define MANTID_ALGORITHMS_POLARIZATIONCORRECTION_TEST_H_ +#ifndef MANTID_ALGORITHMS_POLARIZATIONCORRECTIONFREDRIKZE_TEST_H_ +#define MANTID_ALGORITHMS_POLARIZATIONCORRECTIONFREDRIKZE_TEST_H_ #include <cxxtest/TestSuite.h> -#include "MantidAlgorithms/PolarizationCorrection.h" + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/AnalysisDataService.h" #include "MantidAPI/Axis.h" -#include "MantidDataObjects/Workspace2D.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidAlgorithms/PolarizationCorrectionFredrikze.h" +#include "MantidDataHandling/CreatePolarizationEfficiencies.h" #include "MantidDataObjects/TableWorkspace.h" -#include <boost/make_shared.hpp> +#include "MantidDataObjects/Workspace2D.h" +#include "MantidKernel/OptionalBool.h" + #include "MantidTestHelpers/WorkspaceCreationHelper.h" -#include "MantidAPI/AlgorithmManager.h" -#include "MantidAPI/WorkspaceGroup.h" + +#include <boost/make_shared.hpp> using namespace Mantid::API; using namespace Mantid::Algorithms; +using namespace Mantid::DataHandling; using namespace Mantid::DataObjects; using namespace WorkspaceCreationHelper; -class PolarizationCorrectionTest : public CxxTest::TestSuite { +class PolarizationCorrectionFredrikzeTest : 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 PolarizationCorrectionTest *createSuite() { - return new PolarizationCorrectionTest(); + static PolarizationCorrectionFredrikzeTest *createSuite() { + return new PolarizationCorrectionFredrikzeTest(); + } + static void destroySuite(PolarizationCorrectionFredrikzeTest *suite) { + AnalysisDataService::Instance().clear(); + delete suite; } - static void destroySuite(PolarizationCorrectionTest *suite) { delete suite; } void test_Init() { - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; TS_ASSERT_THROWS_NOTHING(alg.initialize()) TS_ASSERT(alg.isInitialized()) } void test_set_wrong_workspace_type_throws() { MatrixWorkspace_sptr ws = boost::make_shared<Workspace2D>(); - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; TS_ASSERT_THROWS_NOTHING(alg.initialize()); TS_ASSERT_THROWS(alg.setProperty("InputWorkspace", ws), std::invalid_argument &); } void test_set_analysis_to_PA() { - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; TS_ASSERT_THROWS_NOTHING(alg.initialize()); TS_ASSERT_THROWS_NOTHING(alg.setProperty("PolarizationAnalysis", "PA")); } void test_set_analysis_to_PNR() { - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; TS_ASSERT_THROWS_NOTHING(alg.initialize()) TS_ASSERT_THROWS_NOTHING(alg.setProperty("PolarizationAnalysis", "PNR")); } void test_set_analysis_to_invalid_throws() { - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; TS_ASSERT_THROWS_NOTHING(alg.initialize()) TS_ASSERT_THROWS(alg.setProperty("PolarizationAnalysis", "_"), std::invalid_argument &); @@ -64,23 +74,44 @@ public: return group; } + MatrixWorkspace_sptr makeEfficiencies(Workspace_sptr inWS, + const std::string &rho, + const std::string &pp, + const std::string &alpha = "", + const std::string &ap = "") { + CreatePolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", inWS); + alg.setPropertyValue("Rho", rho); + alg.setPropertyValue("Pp", pp); + if (!ap.empty()) { + alg.setPropertyValue("Ap", ap); + alg.setPropertyValue("Alpha", alpha); + } + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + return outWS; + } + void test_throw_if_PA_and_group_is_wrong_size_throws() { Mantid::API::WorkspaceGroup_sptr inWS = boost::make_shared<WorkspaceGroup>(); // Empty group ws. // Name of the output workspace. - std::string outWSName("PolarizationCorrectionTest_OutputWS"); + std::string outWSName("PolarizationCorrectionFredrikzeTest_OutputWS"); + auto efficiencies = makeEfficiencies(create1DWorkspace(4, 1, 1), "1,1,1,1", + "1,1,1,1", "1,1,1,1", "1,1,1,1"); - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; alg.setChild(true); alg.setRethrows(true); alg.initialize(); alg.setProperty("InputWorkspace", inWS); alg.setProperty("PolarizationAnalysis", "PA"); - alg.setPropertyValue("CRho", "1,1,1,1"); - alg.setPropertyValue("CAlpha", "1,1,1,1"); - alg.setPropertyValue("CAp", "1,1,1,1"); - alg.setPropertyValue("CPp", "1,1,1,1"); + alg.setProperty("Efficiencies", efficiencies); alg.setPropertyValue("OutputWorkspace", outWSName); TSM_ASSERT_THROWS("Wrong number of grouped workspaces, should throw", @@ -92,19 +123,18 @@ public: boost::make_shared<WorkspaceGroup>(); // Empty group ws. // Name of the output workspace. - std::string outWSName("PolarizationCorrectionTest_OutputWS"); + std::string outWSName("PolarizationCorrectionFredrikzeTest_OutputWS"); + auto efficiencies = makeEfficiencies(create1DWorkspace(4, 1, 1), "1,1,1,1", + "1,1,1,1", "1,1,1,1", "1,1,1,1"); - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; alg.setChild(true); alg.setRethrows(true); alg.initialize(); alg.setProperty("InputWorkspace", inWS); alg.setProperty("PolarizationAnalysis", "PNR"); alg.setPropertyValue("OutputWorkspace", outWSName); - alg.setPropertyValue("CRho", "1,1,1,1"); - alg.setPropertyValue("CAlpha", "1,1,1,1"); - alg.setPropertyValue("CAp", "1,1,1,1"); - alg.setPropertyValue("CPp", "1,1,1,1"); + alg.setProperty("Efficiencies", efficiencies); TSM_ASSERT_THROWS("Wrong number of grouped workspaces, should throw", alg.execute(), std::invalid_argument &); } @@ -118,19 +148,18 @@ public: // table workspace // Name of the output workspace. - std::string outWSName("PolarizationCorrectionTest_OutputWS"); + std::string outWSName("PolarizationCorrectionFredrikzeTest_OutputWS"); + auto efficiencies = makeEfficiencies(create1DWorkspace(4, 1, 1), "1,1,1,1", + "1,1,1,1", "1,1,1,1", "1,1,1,1"); - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; alg.setChild(true); alg.setRethrows(true); alg.initialize(); alg.setProperty("InputWorkspace", inWS); alg.setProperty("PolarizationAnalysis", "PNR"); alg.setPropertyValue("OutputWorkspace", outWSName); - alg.setPropertyValue("CRho", "1,1,1,1"); - alg.setPropertyValue("CAlpha", "1,1,1,1"); - alg.setPropertyValue("CAp", "1,1,1,1"); - alg.setPropertyValue("CPp", "1,1,1,1"); + alg.setProperty("Efficiencies", efficiencies); TSM_ASSERT_THROWS("Wrong workspace types in group", alg.execute(), std::invalid_argument &); } @@ -149,18 +178,17 @@ public: groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + auto efficiencies = makeEfficiencies(create1DWorkspace(4, 1, 1), "1,0,0,0", + "1,0,0,0", "1,0,0,0", "1,0,0,0"); - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; alg.setChild(true); alg.setRethrows(true); alg.initialize(); alg.setProperty("InputWorkspace", groupWS); alg.setPropertyValue("OutputWorkspace", "dummy"); alg.setProperty("PolarizationAnalysis", "PA"); - alg.setPropertyValue("CRho", "1,0,0,0"); - alg.setPropertyValue("CAlpha", "1,0,0,0"); - alg.setPropertyValue("CAp", "1,0,0,0"); - alg.setPropertyValue("CPp", "1,0,0,0"); + alg.setProperty("Efficiencies", efficiencies); alg.execute(); WorkspaceGroup_sptr outWS = alg.getProperty("OutputWorkspace"); @@ -181,20 +209,91 @@ public: } } + void setInstrument(Workspace_sptr ws, const std::string &instrument_name) { + auto alg = AlgorithmManager::Instance().createUnmanaged("LoadInstrument"); + AnalysisDataService::Instance().addOrReplace("dummy", ws); + alg->initialize(); + alg->setProperty("Workspace", "dummy"); + alg->setProperty("InstrumentName", instrument_name); + alg->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(true)); + alg->execute(); + } + + void test_run_PA_default() { + auto groupWS = boost::make_shared<WorkspaceGroup>(); // Empty group ws. + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + setInstrument(groupWS, "POLREF"); + auto efficiencies = + makeEfficiencies(create1DWorkspace(4, 1, 1), "1,0,0,0", "1,0,0,0"); + + PolarizationCorrectionFredrikze alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", groupWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setProperty("PolarizationAnalysis", "PA"); + alg.setProperty("Efficiencies", efficiencies); + alg.execute(); + WorkspaceGroup_sptr outWS = alg.getProperty("OutputWorkspace"); + + TSM_ASSERT_EQUALS("Wrong number of output workspaces", outWS->size(), + groupWS->size()); + + for (size_t i = 0; i < outWS->size(); ++i) { + std::cout << "Checking equivalent workspaces at index : " << i << '\n'; + auto checkAlg = + AlgorithmManager::Instance().createUnmanaged("CompareWorkspaces"); + checkAlg->initialize(); + checkAlg->setChild(true); + checkAlg->setProperty("Workspace1", groupWS->getItem(i)); + checkAlg->setProperty("Workspace2", outWS->getItem(i)); + checkAlg->setProperty("Tolerance", 3e-16); + checkAlg->execute(); + TS_ASSERT(!checkAlg->getProperty("Result")); + } + } + + void test_run_PA_default_no_instrument_parameters() { + auto groupWS = boost::make_shared<WorkspaceGroup>(); // Empty group ws. + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + auto efficiencies = + makeEfficiencies(create1DWorkspace(4, 1, 1), "1,0,0,0", "1,0,0,0"); + + PolarizationCorrectionFredrikze alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", groupWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setProperty("PolarizationAnalysis", "PA"); + alg.setProperty("Efficiencies", efficiencies); + TSM_ASSERT_THROWS( + "Instrument doesn't have default efficiencies, should throw", + alg.execute(), std::invalid_argument &); + } + void test_run_PNR_unity() { auto groupWS = boost::make_shared<WorkspaceGroup>(); // Empty group ws. groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); groupWS->addWorkspace(create1DWorkspace(4, 1, 1)); + auto efficiencies = + makeEfficiencies(create1DWorkspace(4, 1, 1), "1,0,0,0", "1,0,0,0"); - PolarizationCorrection alg; + PolarizationCorrectionFredrikze alg; alg.setChild(true); alg.setRethrows(true); alg.initialize(); alg.setProperty("InputWorkspace", groupWS); alg.setPropertyValue("OutputWorkspace", "dummy"); alg.setProperty("PolarizationAnalysis", "PNR"); - alg.setPropertyValue("CRho", "1,0,0,0"); - alg.setPropertyValue("CPp", "1,0,0,0"); + alg.setProperty("Efficiencies", efficiencies); alg.execute(); WorkspaceGroup_sptr outWS = alg.getProperty("OutputWorkspace"); @@ -215,4 +314,4 @@ public: } }; -#endif /* MANTID_ALGORITHMS_POLARIZATIONCORRECTION_TEST_H_ */ +#endif /* MANTID_ALGORITHMS_POLARIZATIONCORRECTIONFREDRIKZE_TEST_H_ */ diff --git a/Framework/Algorithms/test/PolarizationCorrectionWildesTest.h b/Framework/Algorithms/test/PolarizationCorrectionWildesTest.h new file mode 100644 index 0000000000000000000000000000000000000000..e962c7c14767ad7feaf1ad9c46c1e816e8fbb214 --- /dev/null +++ b/Framework/Algorithms/test/PolarizationCorrectionWildesTest.h @@ -0,0 +1,1990 @@ +#ifndef MANTID_ALGORITHMS_POLARIZATIONCORRECTIONWILDESTEST_H_ +#define MANTID_ALGORITHMS_POLARIZATIONCORRECTIONWILDESTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAlgorithms/PolarizationCorrectionWildes.h" + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/TextAxis.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" + +#include <Eigen/Dense> + +using Mantid::Algorithms::PolarizationCorrectionWildes; + +class PolarizationCorrectionWildesTest : 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 PolarizationCorrectionWildesTest *createSuite() { + return new PolarizationCorrectionWildesTest(); + } + static void destroySuite(PolarizationCorrectionWildesTest *suite) { + delete suite; + } + + void tearDown() override { + using namespace Mantid::API; + AnalysisDataService::Instance().clear(); + } + + void test_Init() { + PolarizationCorrectionWildes alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + } + + void test_IdealCaseFullCorrections() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nBins{3}; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, 4.2 * yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws01 = ws00->clone(); + MatrixWorkspace_sptr ws10 = ws00->clone(); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{{"ws00", "ws01", "ws10", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 4> wsList{{ws00, ws01, ws10, ws11}}; + for (size_t i = 0; i != 4; ++i) { + for (size_t j = 0; j != nHist; ++j) { + wsList[i]->mutableY(j) *= static_cast<double>(i + 1); + wsList[i]->mutableE(j) *= static_cast<double>(i + 1); + } + AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); + } + auto effWS = idealEfficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) + const std::array<std::string, 4> POL_DIRS{{"++", "+-", "-+", "--"}}; + for (size_t i = 0; i != 4; ++i) { + const std::string wsName = + m_outputWSName + std::string("_") + POL_DIRS[i]; + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(wsName)); + TS_ASSERT(ws) + TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &xs = ws->x(j); + const auto &ys = ws->y(j); + const auto &es = ws->e(j); + TS_ASSERT_EQUALS(ys.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + const double y = counts[k]; + TS_ASSERT_EQUALS(xs[k], edges[k]) + TS_ASSERT_EQUALS(ys[k], y * static_cast<double>(i + 1)) + TS_ASSERT_EQUALS(es[k], std::sqrt(y) * static_cast<double>(i + 1)) + } + } + } + } + + void test_IdealCaseThreeInputs10Missing() { idealThreeInputsTest("10"); } + + void test_IdealCaseThreeInputs01Missing() { idealThreeInputsTest("01"); } + + void test_IdealCaseTwoInputsWithAnalyzer() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nBins{3}; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, 4.2 * yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{ + std::initializer_list<std::string>{"ws00", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; + for (size_t i = 0; i != nHist; ++i) { + ws11->mutableY(i) *= 2.; + ws11->mutableE(i) *= 2.; + } + AnalysisDataService::Instance().addOrReplace(wsNames.front(), + wsList.front()); + AnalysisDataService::Instance().addOrReplace(wsNames.back(), wsList.back()); + auto effWS = idealEfficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "00, 11")) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) + const std::array<std::string, 4> POL_DIRS{{"++", "+-", "-+", "--"}}; + for (size_t i = 0; i != 4; ++i) { + const auto &dir = POL_DIRS[i]; + const std::string wsName = m_outputWSName + std::string("_") + dir; + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(wsName)); + TS_ASSERT(ws) + TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &xs = ws->x(j); + const auto &ys = ws->y(j); + const auto &es = ws->e(j); + TS_ASSERT_EQUALS(ys.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + const double y = counts[k]; + const double expected = [y, &dir]() { + if (dir == "++") { + return y; + } else if (dir == "--") { + return 2. * y; + } else { + return 0.; + } + }(); + const double expectedError = [y, &dir]() { + if (dir == "++") { + return std::sqrt(y); + } else if (dir == "--") { + return 2. * std::sqrt(y); + } else { + return 0.; + } + }(); + TS_ASSERT_EQUALS(xs[k], edges[k]) + TS_ASSERT_EQUALS(ys[k], expected) + TS_ASSERT_EQUALS(es[k], expectedError) + } + } + } + } + + void test_IdealCaseTwoInputsNoAnalyzer() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nBins{3}; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, 4.2 * yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{ + std::initializer_list<std::string>{"ws00", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; + for (size_t i = 0; i != nHist; ++i) { + ws11->mutableY(i) *= 2.; + ws11->mutableE(i) *= 2.; + } + AnalysisDataService::Instance().addOrReplace(wsNames.front(), + wsList.front()); + AnalysisDataService::Instance().addOrReplace(wsNames.back(), wsList.back()); + auto effWS = idealEfficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0, 1")) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 2) + const std::array<std::string, 2> POL_DIRS{{"++", "--"}}; + for (size_t i = 0; i != 2; ++i) { + const auto &dir = POL_DIRS[i]; + const std::string wsName = m_outputWSName + std::string("_") + dir; + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(wsName)); + TS_ASSERT(ws) + TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &xs = ws->x(j); + const auto &ys = ws->y(j); + const auto &es = ws->e(j); + TS_ASSERT_EQUALS(ys.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + const double y = counts[k]; + TS_ASSERT_EQUALS(xs[k], edges[k]) + TS_ASSERT_EQUALS(ys[k], y * static_cast<double>(i + 1)) + TS_ASSERT_EQUALS(es[k], std::sqrt(y) * static_cast<double>(i + 1)) + } + } + } + } + + void test_IdealCaseDirectBeamCorrections() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nBins{3}; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, 4.2 * yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + const std::vector<std::string> wsNames{{"ws00"}}; + AnalysisDataService::Instance().addOrReplace(wsNames.front(), ws00); + auto effWS = idealEfficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0")) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 1) + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_++"))); + TS_ASSERT(ws) + TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) + for (size_t i = 0; i != nHist; ++i) { + const auto &xs = ws->x(i); + const auto &ys = ws->y(i); + const auto &es = ws->e(i); + TS_ASSERT_EQUALS(ys.size(), nBins) + for (size_t j = 0; j != nBins; ++j) { + const double y = counts[j]; + TS_ASSERT_EQUALS(xs[j], edges[j]) + TS_ASSERT_EQUALS(ys[j], y) + TS_ASSERT_EQUALS(es[j], std::sqrt(y)) + } + } + } + + void test_FullCorrections() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws01 = ws00->clone(); + MatrixWorkspace_sptr ws10 = ws00->clone(); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{{"ws00", "ws01", "ws10", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 4> wsList{{ws00, ws01, ws10, ws11}}; + for (size_t i = 0; i != 4; ++i) { + for (size_t j = 0; j != nHist; ++j) { + wsList[i]->mutableY(j) *= static_cast<double>(i + 1); + wsList[i]->mutableE(j) *= static_cast<double>(i + 1); + } + AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); + } + auto effWS = efficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) + fullFourInputsResultsCheck(outputWS, ws00, ws01, ws10, ws11, effWS); + } + + void test_ThreeInputsWithMissing01FlipperConfiguration() { + threeInputsTest("01"); + } + + void test_ThreeInputsWithMissing10FlipperConfiguration() { + threeInputsTest("10"); + } + + void test_TwoInputsWithAnalyzer() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nHist{2}; + constexpr size_t nBins{3}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws01 = nullptr; + MatrixWorkspace_sptr ws10 = nullptr; + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{ + std::initializer_list<std::string>{"ws00", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; + for (size_t i = 0; i != 2; ++i) { + for (size_t j = 0; j != nHist; ++j) { + wsList[i]->mutableY(j) *= static_cast<double>(i + 1); + wsList[i]->mutableE(j) *= static_cast<double>(i + 1); + } + AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); + } + auto effWS = efficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", "00, 11")) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) + solveMissingIntensities(ws00, ws01, ws10, ws11, effWS); + using namespace Mantid::API; + const double F1 = effWS->y(0).front(); + const double F1e = effWS->e(0).front(); + const double F2 = effWS->y(1).front(); + const double F2e = effWS->e(1).front(); + const double P1 = effWS->y(2).front(); + const double P1e = effWS->e(2).front(); + const double P2 = effWS->y(3).front(); + const double P2e = effWS->e(3).front(); + const Eigen::Vector4d y{ws00->y(0).front(), ws01->y(0).front(), + ws10->y(0).front(), ws11->y(0).front()}; + const auto expected = correction(y, F1, F2, P1, P2); + const Eigen::Vector4d e{ws00->e(0).front(), ws01->e(0).front(), + ws10->e(0).front(), ws11->e(0).front()}; + const auto expectedError = error(y, e, F1, F1e, F2, F2e, P1, P1e, P2, P2e); + MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_++"))); + MatrixWorkspace_sptr pmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_+-"))); + MatrixWorkspace_sptr mpWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_-+"))); + MatrixWorkspace_sptr mmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_--"))); + TS_ASSERT(ppWS) + TS_ASSERT(pmWS) + TS_ASSERT(mpWS) + TS_ASSERT(mmWS) + TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) + TS_ASSERT_EQUALS(pmWS->getNumberHistograms(), nHist) + TS_ASSERT_EQUALS(mpWS->getNumberHistograms(), nHist) + TS_ASSERT_EQUALS(mmWS->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &ppX = ppWS->x(j); + const auto &ppY = ppWS->y(j); + const auto &ppE = ppWS->e(j); + const auto &pmX = pmWS->x(j); + const auto &pmY = pmWS->y(j); + const auto &pmE = pmWS->e(j); + const auto &mpX = mpWS->x(j); + const auto &mpY = mpWS->y(j); + const auto &mpE = mpWS->e(j); + const auto &mmX = mmWS->x(j); + const auto &mmY = mmWS->y(j); + const auto &mmE = mmWS->e(j); + TS_ASSERT_EQUALS(ppY.size(), nBins) + TS_ASSERT_EQUALS(pmY.size(), nBins) + TS_ASSERT_EQUALS(mpY.size(), nBins) + TS_ASSERT_EQUALS(mmY.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + TS_ASSERT_EQUALS(ppX[k], edges[k]) + TS_ASSERT_EQUALS(pmX[k], edges[k]) + TS_ASSERT_EQUALS(mpX[k], edges[k]) + TS_ASSERT_EQUALS(mmX[k], edges[k]) + TS_ASSERT_DELTA(ppY[k], expected[0], 1e-12) + TS_ASSERT_DELTA(pmY[k], expected[1], 1e-12) + TS_ASSERT_DELTA(mpY[k], expected[2], 1e-12) + TS_ASSERT_DELTA(mmY[k], expected[3], 1e-12) + // This test constructs the expected missing I01 and I10 intensities + // slightly different from what the algorithm does: I10 is solved + // first and then I01 is solved using all I00, I10 and I11. This + // results in slightly larger errors estimates for I01 and thus for + // the final corrected expected intensities. + TS_ASSERT_DELTA(ppE[k], expectedError[0], 1e-6) + TS_ASSERT_LESS_THAN(ppE[k], expectedError[0]) + TS_ASSERT_DELTA(pmE[k], expectedError[1], 1e-2) + TS_ASSERT_LESS_THAN(pmE[k], expectedError[1]) + TS_ASSERT_DELTA(mpE[k], expectedError[2], 1e-7) + TS_ASSERT_LESS_THAN(mpE[k], expectedError[2]) + TS_ASSERT_DELTA(mmE[k], expectedError[3], 1e-5) + TS_ASSERT_LESS_THAN(mmE[k], expectedError[3]) + } + } + } + + void test_TwoInputsWithoutAnalyzer() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nHist{2}; + constexpr size_t nBins{3}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{ + std::initializer_list<std::string>{"ws00", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; + for (size_t i = 0; i != 2; ++i) { + for (size_t j = 0; j != nHist; ++j) { + wsList[i]->mutableY(j) *= static_cast<double>(i + 1); + wsList[i]->mutableE(j) *= static_cast<double>(i + 1); + } + AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); + } + auto effWS = efficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", "0, 1")) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 2) + const double F1 = effWS->y(0).front(); + const double F1e = effWS->e(0).front(); + const double P1 = effWS->y(2).front(); + const double P1e = effWS->e(2).front(); + const Eigen::Vector2d y{ws00->y(0).front(), ws11->y(0).front()}; + const auto expected = correctionWithoutAnalyzer(y, F1, P1); + const Eigen::Vector2d e{ws00->e(0).front(), ws11->e(0).front()}; + const auto expectedError = errorWithoutAnalyzer(y, e, F1, F1e, P1, P1e); + MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_++"))); + MatrixWorkspace_sptr mmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_--"))); + TS_ASSERT(ppWS) + TS_ASSERT(mmWS) + TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) + TS_ASSERT_EQUALS(mmWS->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &ppX = ppWS->x(j); + const auto &ppY = ppWS->y(j); + const auto &ppE = ppWS->e(j); + const auto &mmX = mmWS->x(j); + const auto &mmY = mmWS->y(j); + const auto &mmE = mmWS->e(j); + TS_ASSERT_EQUALS(ppY.size(), nBins) + TS_ASSERT_EQUALS(mmY.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + TS_ASSERT_EQUALS(ppX[k], edges[k]) + TS_ASSERT_EQUALS(mmX[k], edges[k]) + TS_ASSERT_DELTA(ppY[k], expected[0], 1e-12) + TS_ASSERT_DELTA(mmY[k], expected[1], 1e-12) + TS_ASSERT_DELTA(ppE[k], expectedError[0], 1e-12) + TS_ASSERT_DELTA(mmE[k], expectedError[1], 1e-12) + } + } + } + + void test_directBeamOnlyInput() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nHist{2}; + constexpr size_t nBins{3}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + const std::string wsName{"ws00"}; + AnalysisDataService::Instance().addOrReplace(wsName, ws00); + auto effWS = efficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaces", wsName)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", "0")) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 1) + const auto P1 = effWS->y(2).front(); + const auto P1e = effWS->e(2).front(); + const auto P2 = effWS->y(3).front(); + const auto P2e = effWS->e(3).front(); + const double y{ws00->y(0).front()}; + const auto inverted = 1. / (1. - P2 - P1 + 2. * P1 * P2); + const auto expected = inverted * y; + const double e{ws00->e(0).front()}; + const auto errorP1 = P1e * y * (2. * P1 - 1.) * inverted * inverted; + const auto errorP2 = P2e * y * (2. * P2 - 1.) * inverted * inverted; + const auto errorY = e * e * inverted * inverted; + const auto expectedError = + std::sqrt(errorP1 * errorP1 + errorP2 * errorP2 + errorY); + MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_++"))); + TS_ASSERT(ppWS) + TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &ppX = ppWS->x(j); + const auto &ppY = ppWS->y(j); + const auto &ppE = ppWS->e(j); + TS_ASSERT_EQUALS(ppY.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + TS_ASSERT_EQUALS(ppX[k], edges[k]) + TS_ASSERT_DELTA(ppY[k], expected, 1e-12) + TS_ASSERT_DELTA(ppE[k], expectedError, 1e-12) + } + } + } + + void test_FailureWhenEfficiencyHistogramIsMissing() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + Counts counts{0., 0., 0.}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(1, Histogram(edges, counts)); + const std::string wsName{"ws00"}; + AnalysisDataService::Instance().addOrReplace(wsName, ws00); + auto effWS = idealEfficiencies(edges); + // Rename F1 to something else. + auto axis = make_unique<TextAxis>(4); + axis->setLabel(0, "__wrong_histogram_label"); + axis->setLabel(1, "F2"); + axis->setLabel(2, "P1"); + axis->setLabel(3, "P2"); + effWS->replaceAxis(1, axis.release()); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaces", wsName)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0")) + TS_ASSERT_THROWS(alg.execute(), std::runtime_error) + TS_ASSERT(!alg.isExecuted()) + } + + void test_FailureWhenEfficiencyXDataMismatches() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + Counts counts{0., 0., 0.}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(1, Histogram(edges, counts)); + const std::string wsName{"ws00"}; + AnalysisDataService::Instance().addOrReplace(wsName, ws00); + auto effWS = idealEfficiencies(edges); + // Change a bin edge of one of the histograms. + auto &xs = effWS->mutableX(0); + xs[xs.size() / 2] *= 1.01; + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaces", wsName)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0")) + TS_ASSERT_THROWS(alg.execute(), std::runtime_error) + TS_ASSERT(!alg.isExecuted()) + } + + void test_FailureWhenNumberOfHistogramsInInputWorkspacesMismatch() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + Counts counts{0., 0., 0.}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws01 = ws00->clone(); + MatrixWorkspace_sptr ws10 = + create<Workspace2D>(nHist + 1, Histogram(edges, counts)); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{{"ws00", "ws01", "ws10", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 4> wsList{{ws00, ws01, ws10, ws11}}; + for (size_t i = 0; i != 4; ++i) { + AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); + } + auto effWS = idealEfficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + TS_ASSERT_THROWS(alg.execute(), std::runtime_error) + TS_ASSERT(!alg.isExecuted()) + } + + void test_FailureWhenAnInputWorkspaceIsMissing() { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + Counts counts{0., 0., 0.}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws01 = ws00->clone(); + MatrixWorkspace_sptr ws11 = ws00->clone(); + AnalysisDataService::Instance().addOrReplace("ws00", ws00); + AnalysisDataService::Instance().addOrReplace("ws01", ws01); + AnalysisDataService::Instance().addOrReplace("ws11", ws11); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS( + alg.setPropertyValue("InputWorkspaces", "ws00, ws01, ws10, ws11"), + std::invalid_argument) + } + +private: + const std::string m_outputWSName{"output"}; + + Mantid::API::MatrixWorkspace_sptr + efficiencies(const Mantid::HistogramData::BinEdges &edges) { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + const auto nBins = edges.size() - 1; + constexpr size_t nHist{4}; + Counts counts(nBins, 0.0); + MatrixWorkspace_sptr ws = + create<Workspace2D>(nHist, Histogram(edges, counts)); + ws->mutableY(0) = 0.95; + ws->mutableE(0) = 0.01; + ws->mutableY(1) = 0.92; + ws->mutableE(1) = 0.02; + ws->mutableY(2) = 0.05; + ws->mutableE(2) = 0.015; + ws->mutableY(3) = 0.04; + ws->mutableE(3) = 0.03; + auto axis = make_unique<TextAxis>(4); + axis->setLabel(0, "F1"); + axis->setLabel(1, "F2"); + axis->setLabel(2, "P1"); + axis->setLabel(3, "P2"); + ws->replaceAxis(1, axis.release()); + return ws; + } + + Mantid::API::MatrixWorkspace_sptr + idealEfficiencies(const Mantid::HistogramData::BinEdges &edges) { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + const auto nBins = edges.size() - 1; + constexpr size_t nHist{4}; + Counts counts(nBins, 0.0); + MatrixWorkspace_sptr ws = + create<Workspace2D>(nHist, Histogram(edges, counts)); + ws->mutableY(0) = 1.; + ws->mutableY(1) = 1.; + auto axis = make_unique<TextAxis>(4); + axis->setLabel(0, "F1"); + axis->setLabel(1, "F2"); + axis->setLabel(2, "P1"); + axis->setLabel(3, "P2"); + ws->replaceAxis(1, axis.release()); + return ws; + } + + void idealThreeInputsTest(const std::string &missingFlipperConf) { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nBins{3}; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, 4.2 * yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr wsXX = ws00->clone(); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{{"ws00", "wsXX", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 3> wsList{{ws00, wsXX, ws11}}; + for (size_t i = 0; i != 3; ++i) { + for (size_t j = 0; j != nHist; ++j) { + wsList[i]->mutableY(j) *= static_cast<double>(i + 1); + wsList[i]->mutableE(j) *= static_cast<double>(i + 1); + } + AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); + } + auto effWS = idealEfficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + const std::string presentFlipperConf = + missingFlipperConf == "01" ? "10" : "01"; + const std::string flipperConf = "00, " + presentFlipperConf + ", 11"; + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", flipperConf)) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) + const std::array<std::string, 4> POL_DIRS{{"++", "+-", "-+", "--"}}; + for (size_t i = 0; i != 4; ++i) { + const auto &dir = POL_DIRS[i]; + const std::string wsName = m_outputWSName + std::string("_") + dir; + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(wsName)); + TS_ASSERT(ws) + TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &xs = ws->x(j); + const auto &ys = ws->y(j); + const auto &es = ws->e(j); + TS_ASSERT_EQUALS(ys.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + const double y = counts[k]; + const double expected = [y, &dir]() { + if (dir == "++") { + return y; + } else if (dir == "--") { + return 3. * y; + } else { + return 2. * y; + } + }(); + const double expectedError = [y, &dir, &missingFlipperConf]() { + if (dir == "++") { + return std::sqrt(y); + } else if (dir == "--") { + return 3. * std::sqrt(y); + } else { + std::string conf = std::string(dir.front() == '+' ? "0" : "1") + + std::string(dir.back() == '+' ? "0" : "1"); + if (conf != missingFlipperConf) { + return 2. * std::sqrt(y); + } else { + return 0.; + } + } + }(); + TS_ASSERT_EQUALS(xs[k], edges[k]) + TS_ASSERT_EQUALS(ys[k], expected) + TS_ASSERT_EQUALS(es[k], expectedError) + } + } + } + } + + void threeInputsTest(const std::string &missingFlipperConf) { + using namespace Mantid::API; + using namespace Mantid::DataObjects; + using namespace Mantid::HistogramData; + using namespace Mantid::Kernel; + constexpr size_t nHist{2}; + BinEdges edges{0.3, 0.6, 0.9, 1.2}; + const double yVal = 2.3; + Counts counts{yVal, yVal, yVal}; + MatrixWorkspace_sptr ws00 = + create<Workspace2D>(nHist, Histogram(edges, counts)); + MatrixWorkspace_sptr ws01 = + missingFlipperConf == "01" ? nullptr : ws00->clone(); + MatrixWorkspace_sptr ws10 = + missingFlipperConf == "10" ? nullptr : ws00->clone(); + MatrixWorkspace_sptr ws11 = ws00->clone(); + const std::vector<std::string> wsNames{{"ws00", "wsXX", "ws11"}}; + const std::array<MatrixWorkspace_sptr, 3> wsList{ + {ws00, ws01 != nullptr ? ws01 : ws10, ws11}}; + for (size_t i = 0; i != 3; ++i) { + for (size_t j = 0; j != nHist; ++j) { + wsList[i]->mutableY(j) *= static_cast<double>(i + 1); + wsList[i]->mutableE(j) *= static_cast<double>(i + 1); + } + AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); + } + auto effWS = efficiencies(edges); + PolarizationCorrectionWildes alg; + alg.setChild(true); + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("OutputWorkspace", m_outputWSName)) + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) + const std::string presentFlipperConf = + missingFlipperConf == "01" ? "10" : "01"; + const std::string flipperConf = "00, " + presentFlipperConf + ", 11"; + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", flipperConf)) + TS_ASSERT_THROWS_NOTHING(alg.execute()) + TS_ASSERT(alg.isExecuted()) + WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outputWS) + TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) + solveMissingIntensity(ws00, ws01, ws10, ws11, effWS); + fullFourInputsResultsCheck(outputWS, ws00, ws01, ws10, ws11, effWS); + } + + void fullFourInputsResultsCheck(Mantid::API::WorkspaceGroup_sptr &outputWS, + Mantid::API::MatrixWorkspace_sptr &ws00, + Mantid::API::MatrixWorkspace_sptr &ws01, + Mantid::API::MatrixWorkspace_sptr &ws10, + Mantid::API::MatrixWorkspace_sptr &ws11, + Mantid::API::MatrixWorkspace_sptr &effWS) { + using namespace Mantid::API; + const auto nHist = ws00->getNumberHistograms(); + const auto nBins = ws00->y(0).size(); + const auto edges = ws00->binEdges(0); + const double F1 = effWS->y(0).front(); + const double F1e = effWS->e(0).front(); + const double F2 = effWS->y(1).front(); + const double F2e = effWS->e(1).front(); + const double P1 = effWS->y(2).front(); + const double P1e = effWS->e(2).front(); + const double P2 = effWS->y(3).front(); + const double P2e = effWS->e(3).front(); + const Eigen::Vector4d y{ws00->y(0).front(), ws01->y(0).front(), + ws10->y(0).front(), ws11->y(0).front()}; + const auto expected = correction(y, F1, F2, P1, P2); + const Eigen::Vector4d e{ws00->e(0).front(), ws01->e(0).front(), + ws10->e(0).front(), ws11->e(0).front()}; + const auto expectedError = error(y, e, F1, F1e, F2, F2e, P1, P1e, P2, P2e); + MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_++"))); + MatrixWorkspace_sptr pmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_+-"))); + MatrixWorkspace_sptr mpWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_-+"))); + MatrixWorkspace_sptr mmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( + outputWS->getItem(m_outputWSName + std::string("_--"))); + TS_ASSERT(ppWS) + TS_ASSERT(pmWS) + TS_ASSERT(mpWS) + TS_ASSERT(mmWS) + TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) + TS_ASSERT_EQUALS(pmWS->getNumberHistograms(), nHist) + TS_ASSERT_EQUALS(mpWS->getNumberHistograms(), nHist) + TS_ASSERT_EQUALS(mmWS->getNumberHistograms(), nHist) + for (size_t j = 0; j != nHist; ++j) { + const auto &ppX = ppWS->x(j); + const auto &ppY = ppWS->y(j); + const auto &ppE = ppWS->e(j); + const auto &pmX = pmWS->x(j); + const auto &pmY = pmWS->y(j); + const auto &pmE = pmWS->e(j); + const auto &mpX = mpWS->x(j); + const auto &mpY = mpWS->y(j); + const auto &mpE = mpWS->e(j); + const auto &mmX = mmWS->x(j); + const auto &mmY = mmWS->y(j); + const auto &mmE = mmWS->e(j); + TS_ASSERT_EQUALS(ppY.size(), nBins) + TS_ASSERT_EQUALS(pmY.size(), nBins) + TS_ASSERT_EQUALS(mpY.size(), nBins) + TS_ASSERT_EQUALS(mmY.size(), nBins) + for (size_t k = 0; k != nBins; ++k) { + TS_ASSERT_EQUALS(ppX[k], edges[k]) + TS_ASSERT_EQUALS(pmX[k], edges[k]) + TS_ASSERT_EQUALS(mpX[k], edges[k]) + TS_ASSERT_EQUALS(mmX[k], edges[k]) + TS_ASSERT_DELTA(ppY[k], expected[0], 1e-12) + TS_ASSERT_DELTA(pmY[k], expected[1], 1e-12) + TS_ASSERT_DELTA(mpY[k], expected[2], 1e-12) + TS_ASSERT_DELTA(mmY[k], expected[3], 1e-12) + TS_ASSERT_DELTA(ppE[k], expectedError[0], 1e-12) + TS_ASSERT_DELTA(pmE[k], expectedError[1], 1e-12) + TS_ASSERT_DELTA(mpE[k], expectedError[2], 1e-12) + TS_ASSERT_DELTA(mmE[k], expectedError[3], 1e-12) + } + } + } + Eigen::Matrix4d invertedF1(const double f1) { + Eigen::Matrix4d m; + m << f1, 0., 0., 0., 0., f1, 0., 0., f1 - 1., 0., 1., 0., 0., f1 - 1., 0., + 1.; + m *= 1. / f1; + return m; + } + + Eigen::Matrix4d invertedF1Derivative(const double f1) { + Eigen::Matrix4d m; + m << 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., -1., 0., 0., 1., 0., -1.; + m *= 1. / (f1 * f1); + return m; + } + + Eigen::Matrix4d invertedF2(const double f2) { + Eigen::Matrix4d m; + m << f2, 0., 0., 0., f2 - 1., 1., 0., 0., 0., 0., f2, 0., 0., 0., f2 - 1., + 1.; + m *= 1. / f2; + return m; + } + + Eigen::Matrix4d invertedF2Derivative(const double f2) { + Eigen::Matrix4d m; + m << 0., 0., 0., 0., 1., -1., 0., 0., 0., 0., 0., 0., 0., 0., 1., -1.; + m *= 1. / (f2 * f2); + return m; + } + + Eigen::Matrix4d invertedP1(const double p1) { + Eigen::Matrix4d m; + m << p1 - 1., 0., p1, 0., 0., p1 - 1., 0., p1, p1, 0., p1 - 1., 0., 0., p1, + 0., p1 - 1.; + m *= 1. / (2. * p1 - 1.); + return m; + } + + Eigen::Matrix4d invertedP1Derivative(const double p1) { + Eigen::Matrix4d m; + m << 1., 0., -1., 0., 0., 1., 0., -1., -1., 0., 1., 0., 0., -1., 0., 1.; + m *= 1. / (2. * p1 - 1.) / (2. * p1 - 1.); + return m; + } + + Eigen::Matrix4d invertedP2(const double p2) { + Eigen::Matrix4d m; + m << p2 - 1., p2, 0., 0., p2, p2 - 1., 0., 0., 0., 0., p2 - 1., p2, 0., 0., + p2, p2 - 1.; + m *= 1. / (2. * p2 - 1.); + return m; + } + + Eigen::Matrix4d invertedP2Derivative(const double p2) { + Eigen::Matrix4d m; + m << 1., -1., 0., 0., -1., 1., 0., 0., 0., 0., 1., -1., 0., 0., -1., 1.; + m *= 1. / (2. * p2 - 1.) / (2. * p2 - 1.); + return m; + } + + Eigen::Vector4d correction(const Eigen::Vector4d &y, const double f1, + const double f2, const double p1, + const double p2) { + const Eigen::Matrix4d F1 = invertedF1(f1); + const Eigen::Matrix4d F2 = invertedF2(f2); + const Eigen::Matrix4d P1 = invertedP1(p1); + const Eigen::Matrix4d P2 = invertedP2(p2); + const Eigen::Matrix4d inverted = (P2 * P1 * F2 * F1).matrix(); + return (inverted * y).matrix(); + } + + Eigen::Vector4d error(const Eigen::Vector4d &y, const Eigen::Vector4d &e, + const double f1, const double f1e, const double f2, + const double f2e, const double p1, const double p1e, + const double p2, const double p2e) { + const Eigen::Matrix4d F1 = invertedF1(f1); + const Eigen::Matrix4d dF1 = f1e * invertedF1Derivative(f1); + const Eigen::Matrix4d F2 = invertedF2(f2); + const Eigen::Matrix4d dF2 = f2e * invertedF2Derivative(f2); + const Eigen::Matrix4d P1 = invertedP1(p1); + const Eigen::Matrix4d dP1 = p1e * invertedP1Derivative(p1); + const Eigen::Matrix4d P2 = invertedP2(p2); + const Eigen::Matrix4d dP2 = p2e * invertedP2Derivative(p2); + const auto p2Error = (dP2 * P1 * F2 * F1 * y).array(); + const auto p1Error = (P2 * dP1 * F2 * F1 * y).array(); + const auto f2Error = (P2 * P1 * dF2 * F1 * y).array(); + const auto f1Error = (P2 * P1 * F2 * dF1 * y).array(); + const auto inverted = (P2 * P1 * F2 * F1).array(); + const auto yError = ((inverted * inverted).matrix() * + (e.array() * e.array()).matrix()).array(); + return (p2Error * p2Error + p1Error * p1Error + f2Error * f2Error + + f1Error * f1Error + yError) + .sqrt() + .matrix(); + } + + Eigen::Vector2d correctionWithoutAnalyzer(const Eigen::Vector2d &y, + const double f1, const double p1) { + Eigen::Matrix2d F1; + F1 << f1, 0., f1 - 1., 1.; + F1 *= 1. / f1; + Eigen::Matrix2d P1; + P1 << p1 - 1., p1, p1, p1 - 1.; + P1 *= 1. / (2. * p1 - 1.); + const Eigen::Matrix2d inverted = (P1 * F1).matrix(); + return static_cast<Eigen::Vector2d>(inverted * y); + } + + Eigen::Vector2d errorWithoutAnalyzer(const Eigen::Vector2d &y, + const Eigen::Vector2d &e, + const double f1, const double f1e, + const double p1, const double p1e) { + Eigen::Matrix2d F1; + F1 << f1, 0, f1 - 1., 1.; + F1 *= 1. / f1; + Eigen::Matrix2d dF1; + dF1 << 0., 0., 1., -1.; + dF1 *= f1e / (f1 * f1); + Eigen::Matrix2d P1; + P1 << p1 - 1., p1, p1, p1 - 1.; + P1 *= 1. / (2. * p1 - 1.); + Eigen::Matrix2d dP1; + dP1 << 1., -1., -1., 1.; + dP1 *= p1e / ((2. * p1 - 1.) * (2. * p1 - 1.)); + const auto p1Error = (dP1 * F1 * y).array(); + const auto f1Error = (P1 * dF1 * y).array(); + const auto inverted = (P1 * F1).array(); + const auto yError = ((inverted * inverted).matrix() * + (e.array() * e.array()).matrix()).array(); + return (p1Error * p1Error + f1Error * f1Error + yError).sqrt().matrix(); + } + + void solveMissingIntensity(const Mantid::API::MatrixWorkspace_sptr &ppWS, + Mantid::API::MatrixWorkspace_sptr &pmWS, + Mantid::API::MatrixWorkspace_sptr &mpWS, + const Mantid::API::MatrixWorkspace_sptr &mmWS, + const Mantid::API::MatrixWorkspace_sptr &effWS) { + const auto &F1 = effWS->y(0); + const auto &F2 = effWS->y(1); + const auto &P1 = effWS->y(2); + const auto &P2 = effWS->y(3); + if (!pmWS) { + pmWS = mpWS->clone(); + for (size_t wsIndex = 0; wsIndex != pmWS->getNumberHistograms(); + ++wsIndex) { + const auto &ppY = ppWS->y(wsIndex); + auto &pmY = pmWS->mutableY(wsIndex); + auto &pmE = pmWS->mutableE(wsIndex); + const auto &mpY = mpWS->y(wsIndex); + const auto &mmY = mmWS->y(wsIndex); + for (size_t binIndex = 0; binIndex != mpY.size(); ++binIndex) { + pmY[binIndex] = + -(2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - + P2[binIndex] * mmY[binIndex] - + 2 * mpY[binIndex] * F2[binIndex] * P2[binIndex] + + mpY[binIndex] * P2[binIndex] - ppY[binIndex] * P2[binIndex] + + P1[binIndex] * mmY[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + + ppY[binIndex] * P1[binIndex] - P1[binIndex] * mpY[binIndex] + + ppY[binIndex] * F1[binIndex] + mpY[binIndex] * F2[binIndex] - + ppY[binIndex] * F2[binIndex]) / + (P2[binIndex] - P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]); + // Error propagation is not implemented in the algorithm. + pmE[binIndex] = 0.; + } + } + } else { + mpWS = pmWS->clone(); + for (size_t wsIndex = 0; wsIndex != mpWS->getNumberHistograms(); + ++wsIndex) { + const auto &ppY = ppWS->y(wsIndex); + const auto &pmY = pmWS->y(wsIndex); + auto &mpY = mpWS->mutableY(wsIndex); + auto &mpE = mpWS->mutableE(wsIndex); + const auto &mmY = mmWS->y(wsIndex); + for (size_t binIndex = 0; binIndex != mpY.size(); ++binIndex) { + mpY[binIndex] = + (-ppY[binIndex] * P2[binIndex] + P2[binIndex] * pmY[binIndex] - + P2[binIndex] * mmY[binIndex] + + 2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - + pmY[binIndex] * P1[binIndex] + P1[binIndex] * mmY[binIndex] + + ppY[binIndex] * P1[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + + 2 * pmY[binIndex] * F1[binIndex] * P1[binIndex] + + ppY[binIndex] * F1[binIndex] - ppY[binIndex] * F2[binIndex] - + pmY[binIndex] * F1[binIndex]) / + (-P2[binIndex] + 2 * F2[binIndex] * P2[binIndex] + P1[binIndex] - + F2[binIndex]); + // Error propagation is not implemented in the algorithm. + mpE[binIndex] = 0.; + } + } + } + } + + void solveMissingIntensities(const Mantid::API::MatrixWorkspace_sptr &ppWS, + Mantid::API::MatrixWorkspace_sptr &pmWS, + Mantid::API::MatrixWorkspace_sptr &mpWS, + const Mantid::API::MatrixWorkspace_sptr &mmWS, + const Mantid::API::MatrixWorkspace_sptr &effWS) { + const auto &F1 = effWS->y(0); + const auto &F1E = effWS->e(0); + const auto &F2 = effWS->y(1); + const auto &F2E = effWS->e(1); + const auto &P1 = effWS->y(2); + const auto &P1E = effWS->e(2); + const auto &P2 = effWS->y(3); + const auto &P2E = effWS->e(3); + pmWS = ppWS->clone(); + mpWS = ppWS->clone(); + for (size_t wsIndex = 0; wsIndex != ppWS->getNumberHistograms(); + ++wsIndex) { + const auto &ppY = ppWS->y(wsIndex); + const auto &ppE = ppWS->e(wsIndex); + auto &pmY = pmWS->mutableY(wsIndex); + auto &pmE = pmWS->mutableE(wsIndex); + auto &mpY = mpWS->mutableY(wsIndex); + auto &mpE = mpWS->mutableE(wsIndex); + const auto &mmY = mmWS->y(wsIndex); + const auto &mmE = mmWS->e(wsIndex); + for (size_t binIndex = 0; binIndex != mpY.size(); ++binIndex) { + const double P12 = P1[binIndex] * P1[binIndex]; + const double P13 = P1[binIndex] * P12; + const double P14 = P1[binIndex] * P13; + const double P22 = P2[binIndex] * P2[binIndex]; + const double P23 = P2[binIndex] * P22; + const double F12 = F1[binIndex] * F1[binIndex]; + { + mpY[binIndex] = + -(-mmY[binIndex] * P22 * F1[binIndex] + + 2 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * + P2[binIndex] + + 2 * ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] + + 8 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] + + 2 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] - + 8 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * + P1[binIndex] - + 2 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] + + mmY[binIndex] * P2[binIndex] * F1[binIndex] + + ppY[binIndex] * F1[binIndex] * F2[binIndex] - + ppY[binIndex] * F2[binIndex] * P12 + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 + + 4 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] - + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + + ppY[binIndex] * F2[binIndex] * P1[binIndex] - + 4 * ppY[binIndex] * F12 * F2[binIndex] * P12 - + ppY[binIndex] * F12 * F2[binIndex]) / + (-F1[binIndex] * F2[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P22 - + 2 * P22 * F1[binIndex] * P1[binIndex] + + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - + P2[binIndex] * F1[binIndex] + P22 * F1[binIndex] + + F2[binIndex] * P12 - 2 * F2[binIndex] * P12 * P2[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P12 - + F2[binIndex] * P1[binIndex] - + 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); + const double dI00 = + -F2[binIndex] * + (-2 * P2[binIndex] * F1[binIndex] + 2 * P12 * P2[binIndex] + + 8 * P2[binIndex] * F1[binIndex] * P1[binIndex] - + 2 * P1[binIndex] * P2[binIndex] + 2 * P2[binIndex] * F12 - + 8 * P2[binIndex] * F12 * P1[binIndex] - + 8 * P2[binIndex] * F1[binIndex] * P12 + + 8 * P2[binIndex] * F12 * P12 - 4 * F1[binIndex] * P1[binIndex] - + F12 + 4 * F12 * P1[binIndex] + P1[binIndex] + F1[binIndex] - + P12 + 4 * F1[binIndex] * P12 - 4 * F12 * P12) / + (-P2[binIndex] * F1[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * P22 * F1[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P22 - + 2 * F2[binIndex] * P12 * P2[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P12 + + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + + P22 * F1[binIndex] + F2[binIndex] * P12 + + 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - + F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - + 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); + const double dI11 = + -P2[binIndex] * F1[binIndex] * + (1 - 2 * P1[binIndex] - P2[binIndex] + + 2 * P1[binIndex] * P2[binIndex]) / + (-P2[binIndex] * F1[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * P22 * F1[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P22 - + 2 * F2[binIndex] * P12 * P2[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P12 + + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + + P22 * F1[binIndex] + F2[binIndex] * P12 + + 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - + F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - + 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); + const double divisor1 = + (-P2[binIndex] * F1[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * P22 * F1[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P22 - + 2 * F2[binIndex] * P12 * P2[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P12 + + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + + P22 * F1[binIndex] + F2[binIndex] * P12 + + 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - + F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - + 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); + const double dF1 = + -F2[binIndex] * + (-P1[binIndex] * mmY[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P22 - + ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] - + 10 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 - + 8 * ppY[binIndex] * F2[binIndex] * P12 * P22 + + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] - + ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - + 32 * ppY[binIndex] * F12 * F2[binIndex] * P14 * P2[binIndex] + + 32 * ppY[binIndex] * F2[binIndex] * P14 * P2[binIndex] * + F1[binIndex] - + 32 * ppY[binIndex] * F2[binIndex] * P14 * P22 * F1[binIndex] + + 32 * ppY[binIndex] * F12 * F2[binIndex] * P14 * P22 + + 32 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P23 + + 2 * ppY[binIndex] * F2[binIndex] * P14 + + 4 * ppY[binIndex] * P13 * P23 - 4 * P13 * mmY[binIndex] * P23 - + 8 * ppY[binIndex] * F2[binIndex] * P13 * P23 - + 16 * ppY[binIndex] * P23 * F12 * P13 + + 8 * ppY[binIndex] * F12 * F2[binIndex] * P14 - + 8 * ppY[binIndex] * F2[binIndex] * P14 * P2[binIndex] + + 8 * ppY[binIndex] * F2[binIndex] * P14 * P22 - + 8 * ppY[binIndex] * F2[binIndex] * P14 * F1[binIndex] + + 10 * ppY[binIndex] * F2[binIndex] * P13 * P2[binIndex] - + 4 * ppY[binIndex] * F2[binIndex] * P13 * P22 + + 16 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 - + 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P23 + + 12 * ppY[binIndex] * F2[binIndex] * P12 * P23 + + 18 * ppY[binIndex] * P22 * F12 * P1[binIndex] - + 20 * ppY[binIndex] * F12 * F2[binIndex] * P13 - + 36 * ppY[binIndex] * P22 * F12 * P12 + + 24 * ppY[binIndex] * P22 * F12 * P13 - + 6 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] - + 5 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] + + 8 * ppY[binIndex] * F12 * F2[binIndex] * P22 - + 8 * ppY[binIndex] * P2[binIndex] * F12 * P13 + + 12 * ppY[binIndex] * P2[binIndex] * F12 * P12 + + 18 * ppY[binIndex] * F12 * F2[binIndex] * P12 - + 7 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] - + 12 * ppY[binIndex] * P23 * F12 * P1[binIndex] + + 24 * ppY[binIndex] * P23 * F12 * P12 - + 4 * ppY[binIndex] * F12 * F2[binIndex] * P23 - + 3 * ppY[binIndex] * P1[binIndex] * P22 + + ppY[binIndex] * F2[binIndex] * P12 - + 3 * ppY[binIndex] * P12 * P2[binIndex] + + 3 * P12 * mmY[binIndex] * P2[binIndex] - + 9 * P12 * mmY[binIndex] * P22 + 9 * ppY[binIndex] * P12 * P22 + + ppY[binIndex] * P1[binIndex] * P2[binIndex] + + 3 * P1[binIndex] * mmY[binIndex] * P22 - + 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] + + 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P22 + + 40 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * + P2[binIndex] - + 40 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P22 - + 64 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * + P2[binIndex] + + 64 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * P22 + + 34 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * + P1[binIndex] - + 52 * ppY[binIndex] * F12 * F2[binIndex] * P22 * P1[binIndex] - + 84 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] + + 120 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P22 + + 88 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P2[binIndex] - + 112 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P22 + + 24 * ppY[binIndex] * F12 * F2[binIndex] * P23 * P1[binIndex] - + 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P23 + + 2 * ppY[binIndex] * P13 * P2[binIndex] - + 6 * ppY[binIndex] * P13 * P22 - + 3 * ppY[binIndex] * F2[binIndex] * P13 + + 2 * ppY[binIndex] * P1[binIndex] * P23 - + 6 * ppY[binIndex] * P12 * P23 + + ppY[binIndex] * P2[binIndex] * F12 - + 3 * ppY[binIndex] * P22 * F12 + + ppY[binIndex] * F12 * F2[binIndex] + + 2 * ppY[binIndex] * P23 * F12 - + 2 * P13 * mmY[binIndex] * P2[binIndex] + + 6 * P13 * mmY[binIndex] * P22 + 6 * P12 * mmY[binIndex] * P23 - + 2 * P1[binIndex] * mmY[binIndex] * P23) / + (divisor1 * divisor1); + const double divisor2 = + (-P2[binIndex] * F1[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * P22 * F1[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P22 - + 2 * F2[binIndex] * P12 * P2[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P12 + + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + + P22 * F1[binIndex] + F2[binIndex] * P12 + + 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - + F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - + 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); + const double dF2 = + P2[binIndex] * F1[binIndex] * + (3 * P1[binIndex] * mmY[binIndex] * P2[binIndex] - + 12 * ppY[binIndex] * P22 * F1[binIndex] * P1[binIndex] - + 36 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P12 + + 24 * ppY[binIndex] * P22 * F1[binIndex] * P12 + + 18 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + + 12 * ppY[binIndex] * F1[binIndex] * P12 + + 24 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P13 - + 16 * ppY[binIndex] * P22 * F1[binIndex] * P13 + + 12 * ppY[binIndex] * P22 * F12 * P1[binIndex] - + 24 * ppY[binIndex] * P22 * F12 * P12 + + 16 * ppY[binIndex] * P22 * F12 * P13 - + 18 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] - + 24 * ppY[binIndex] * P2[binIndex] * F12 * P13 + + 36 * ppY[binIndex] * P2[binIndex] * F12 * P12 - + 19 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] + + 28 * F1[binIndex] * P12 * mmY[binIndex] * P2[binIndex] - + 12 * F1[binIndex] * P13 * mmY[binIndex] * P2[binIndex] + + 22 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 - + 28 * F1[binIndex] * P12 * mmY[binIndex] * P22 + + 8 * F1[binIndex] * P13 * mmY[binIndex] * P22 - + 8 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P23 + + 8 * F1[binIndex] * P12 * mmY[binIndex] * P23 - + ppY[binIndex] * F12 + 2 * ppY[binIndex] * P13 - + 2 * P13 * mmY[binIndex] - mmY[binIndex] * F1[binIndex] + + 2 * ppY[binIndex] * P1[binIndex] * P22 + + 9 * ppY[binIndex] * P12 * P2[binIndex] - + 9 * P12 * mmY[binIndex] * P2[binIndex] + + 6 * P12 * mmY[binIndex] * P22 - 6 * ppY[binIndex] * P12 * P22 - + 3 * ppY[binIndex] * P1[binIndex] * P2[binIndex] - + 2 * P1[binIndex] * mmY[binIndex] * P22 - + 6 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + + 2 * ppY[binIndex] * P22 * F1[binIndex] - + 3 * ppY[binIndex] * P2[binIndex] * F1[binIndex] - + P1[binIndex] * mmY[binIndex] + ppY[binIndex] * P1[binIndex] - + 3 * ppY[binIndex] * P12 + ppY[binIndex] * F1[binIndex] + + 3 * P12 * mmY[binIndex] - + 6 * ppY[binIndex] * P13 * P2[binIndex] + + 4 * ppY[binIndex] * P13 * P22 + + 3 * ppY[binIndex] * P2[binIndex] * F12 - + 2 * ppY[binIndex] * P22 * F12 + + 5 * F1[binIndex] * P1[binIndex] * mmY[binIndex] + + 6 * ppY[binIndex] * F12 * P1[binIndex] - + 8 * F1[binIndex] * P12 * mmY[binIndex] - + 12 * F12 * P12 * ppY[binIndex] - + 8 * ppY[binIndex] * F1[binIndex] * P13 + + 6 * P13 * mmY[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * P13 * mmY[binIndex] + + 8 * F12 * P13 * ppY[binIndex] - 4 * P13 * mmY[binIndex] * P22 - + 5 * mmY[binIndex] * P22 * F1[binIndex] + + 2 * mmY[binIndex] * P23 * F1[binIndex] + + 4 * mmY[binIndex] * P2[binIndex] * F1[binIndex]) / + (divisor2 * divisor2); + const double divisor3 = + (-P2[binIndex] * F1[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * P22 * F1[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P22 - + 2 * F2[binIndex] * P12 * P2[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P12 + + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + + P22 * F1[binIndex] + F2[binIndex] * P12 + + 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - + F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - + 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); + const double dP1 = + -F1[binIndex] * F2[binIndex] * + (-2 * P1[binIndex] * mmY[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] + + 8 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 24 * ppY[binIndex] * P22 * F1[binIndex] * P1[binIndex] + + 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P22 + + 8 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P12 + + 6 * ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 - + 24 * ppY[binIndex] * P22 * F1[binIndex] * P12 - + 12 * ppY[binIndex] * F2[binIndex] * P12 * P22 - + 8 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + ppY[binIndex] * F2[binIndex] * P2[binIndex] - + 4 * ppY[binIndex] * F2[binIndex] * P22 - + 8 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P23 - + 16 * ppY[binIndex] * P23 * F1[binIndex] * P1[binIndex] - + 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P23 + + 16 * ppY[binIndex] * P23 * F1[binIndex] * P12 + + 8 * ppY[binIndex] * F2[binIndex] * P12 * P23 - + 24 * ppY[binIndex] * P22 * F12 * P1[binIndex] + + 24 * ppY[binIndex] * P22 * F12 * P12 + + 8 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] + + 6 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] - + 12 * ppY[binIndex] * F12 * F2[binIndex] * P22 - + 8 * ppY[binIndex] * P2[binIndex] * F12 * P12 - + 4 * ppY[binIndex] * F12 * F2[binIndex] * P12 + + 4 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] + + 16 * ppY[binIndex] * P23 * F12 * P1[binIndex] - + 16 * ppY[binIndex] * P23 * F12 * P12 + + 8 * ppY[binIndex] * F12 * F2[binIndex] * P23 + + 4 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] - + 4 * F1[binIndex] * P12 * mmY[binIndex] * P2[binIndex] - + 12 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 + + 12 * F1[binIndex] * P12 * mmY[binIndex] * P22 + + 8 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P23 - + 8 * F1[binIndex] * P12 * mmY[binIndex] * P23 + + 2 * mmY[binIndex] * P23 - 2 * ppY[binIndex] * P23 + + 4 * ppY[binIndex] * F2[binIndex] * P23 - + 6 * ppY[binIndex] * P1[binIndex] * P22 - + ppY[binIndex] * F2[binIndex] * P12 - + 2 * ppY[binIndex] * P12 * P2[binIndex] + + 2 * P12 * mmY[binIndex] * P2[binIndex] - + 6 * P12 * mmY[binIndex] * P22 + 6 * ppY[binIndex] * P12 * P22 + + 2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] - + ppY[binIndex] * P2[binIndex] + + 6 * P1[binIndex] * mmY[binIndex] * P22 - + 6 * ppY[binIndex] * P22 * F1[binIndex] + + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + + 3 * ppY[binIndex] * P22 + + 16 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] - + 40 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P22 - + 24 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * + P2[binIndex] + + 48 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P22 + + mmY[binIndex] * P2[binIndex] - 3 * mmY[binIndex] * P22 + + 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P23 - + 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P23 - + 24 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * + P1[binIndex] + + 48 * ppY[binIndex] * F12 * F2[binIndex] * P22 * P1[binIndex] + + 24 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] - + 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P22 - + 32 * ppY[binIndex] * F12 * F2[binIndex] * P23 * P1[binIndex] + + 32 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P23 + + 4 * ppY[binIndex] * P1[binIndex] * P23 + + 4 * ppY[binIndex] * P23 * F1[binIndex] - + 4 * ppY[binIndex] * P12 * P23 - + 2 * ppY[binIndex] * P2[binIndex] * F12 + + 6 * ppY[binIndex] * P22 * F12 - + ppY[binIndex] * F12 * F2[binIndex] - + 4 * ppY[binIndex] * P23 * F12 + 4 * P12 * mmY[binIndex] * P23 - + 4 * P1[binIndex] * mmY[binIndex] * P23 + + 3 * mmY[binIndex] * P22 * F1[binIndex] - + 2 * mmY[binIndex] * P23 * F1[binIndex] - + mmY[binIndex] * P2[binIndex] * F1[binIndex]) / + (divisor3 * divisor3); + const double divisor4 = + (-P2[binIndex] * F1[binIndex] + + 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * P22 * F1[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P22 - + 2 * F2[binIndex] * P12 * P2[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P12 + + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + + P22 * F1[binIndex] + F2[binIndex] * P12 + + 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - + F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - + 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); + const double dP2 = + F1[binIndex] * F2[binIndex] * + (-2 * P1[binIndex] * mmY[binIndex] * P2[binIndex] - + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P22 + + 12 * ppY[binIndex] * P22 * F1[binIndex] * P1[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P22 + + 24 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P12 + + 12 * ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] + + 12 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 - + 24 * ppY[binIndex] * P22 * F1[binIndex] * P12 - + 12 * ppY[binIndex] * F2[binIndex] * P12 * P22 - + 12 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] - + 6 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] - + 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - + 12 * ppY[binIndex] * F1[binIndex] * P12 - + 16 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P13 + + 16 * ppY[binIndex] * P22 * F1[binIndex] * P13 - + 8 * ppY[binIndex] * F2[binIndex] * P13 * P2[binIndex] + + 8 * ppY[binIndex] * F2[binIndex] * P13 * P22 - + 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 - + 12 * ppY[binIndex] * P22 * F12 * P1[binIndex] + + 8 * ppY[binIndex] * F12 * F2[binIndex] * P13 + + 24 * ppY[binIndex] * P22 * F12 * P12 - + 16 * ppY[binIndex] * P22 * F12 * P13 + + 12 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] + + 4 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] - + 4 * ppY[binIndex] * F12 * F2[binIndex] * P22 + + 16 * ppY[binIndex] * P2[binIndex] * F12 * P13 - + 24 * ppY[binIndex] * P2[binIndex] * F12 * P12 - + 12 * ppY[binIndex] * F12 * F2[binIndex] * P12 + + 6 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] + + 10 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] - + 16 * F1[binIndex] * P12 * mmY[binIndex] * P2[binIndex] + + 8 * F1[binIndex] * P13 * mmY[binIndex] * P2[binIndex] - + 6 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 + + 12 * F1[binIndex] * P12 * mmY[binIndex] * P22 - + 8 * F1[binIndex] * P13 * mmY[binIndex] * P22 + + ppY[binIndex] * F12 - 2 * ppY[binIndex] * P13 + + 2 * P13 * mmY[binIndex] + mmY[binIndex] * F1[binIndex] - + 2 * ppY[binIndex] * P1[binIndex] * P22 + + ppY[binIndex] * F2[binIndex] * P1[binIndex] - + 3 * ppY[binIndex] * F2[binIndex] * P12 - + 6 * ppY[binIndex] * P12 * P2[binIndex] + + 6 * P12 * mmY[binIndex] * P2[binIndex] - + 6 * P12 * mmY[binIndex] * P22 + 6 * ppY[binIndex] * P12 * P22 + + 2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] + + ppY[binIndex] * F1[binIndex] * F2[binIndex] + + 2 * P1[binIndex] * mmY[binIndex] * P22 + + 6 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - + 2 * ppY[binIndex] * P22 * F1[binIndex] + + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + + 24 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] - + 24 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P22 - + 48 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * + P2[binIndex] + + 48 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P22 + + P1[binIndex] * mmY[binIndex] - ppY[binIndex] * P1[binIndex] + + 3 * ppY[binIndex] * P12 - ppY[binIndex] * F1[binIndex] - + 3 * P12 * mmY[binIndex] + + 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * + P2[binIndex] - + 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * P22 - + 24 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * + P1[binIndex] + + 24 * ppY[binIndex] * F12 * F2[binIndex] * P22 * P1[binIndex] + + 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] - + 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P22 - + 32 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P2[binIndex] + + 32 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P22 + + 4 * ppY[binIndex] * P13 * P2[binIndex] - + 4 * ppY[binIndex] * P13 * P22 + + 2 * ppY[binIndex] * F2[binIndex] * P13 - + 2 * ppY[binIndex] * P2[binIndex] * F12 + + 2 * ppY[binIndex] * P22 * F12 - + ppY[binIndex] * F12 * F2[binIndex] - + 5 * F1[binIndex] * P1[binIndex] * mmY[binIndex] - + 6 * ppY[binIndex] * F12 * P1[binIndex] + + 8 * F1[binIndex] * P12 * mmY[binIndex] + + 12 * F12 * P12 * ppY[binIndex] + + 8 * ppY[binIndex] * F1[binIndex] * P13 - + 4 * P13 * mmY[binIndex] * P2[binIndex] - + 4 * F1[binIndex] * P13 * mmY[binIndex] - + 8 * F12 * P13 * ppY[binIndex] + 4 * P13 * mmY[binIndex] * P22 + + mmY[binIndex] * P22 * F1[binIndex] - + 2 * mmY[binIndex] * P2[binIndex] * F1[binIndex]) / + (divisor4 * divisor4); + const double e1 = dI00 * ppE[binIndex]; + const double e2 = dI11 * mmE[binIndex]; + const double e3 = dF1 * F1E[binIndex]; + const double e4 = dF2 * F2E[binIndex]; + const double e5 = dP1 * P1E[binIndex]; + const double e6 = dP2 * P2E[binIndex]; + mpE[binIndex] = std::sqrt(e1 * e1 + e2 * e2 + e3 * e3 + e4 * e4 + + e5 * e5 + e6 * e6); + } + { + pmY[binIndex] = + -(ppY[binIndex] * P2[binIndex] * F1[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + + 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + + ppY[binIndex] * P1[binIndex] * P2[binIndex] - + P1[binIndex] * mpY[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] + + P1[binIndex] * mmY[binIndex] * P2[binIndex] - + ppY[binIndex] * F1[binIndex] + + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - + P1[binIndex] * mmY[binIndex] - + P1[binIndex] * mpY[binIndex] * F2[binIndex] + + ppY[binIndex] * F2[binIndex] * P1[binIndex] + + ppY[binIndex] * F1[binIndex] * F2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + + P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])); + const double dI00 = + -(-P1[binIndex] + P1[binIndex] * P2[binIndex] + + F2[binIndex] * P1[binIndex] - + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] + + 2 * F1[binIndex] * P1[binIndex] - + 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P1[binIndex] + + 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + + F1[binIndex] * F2[binIndex] - F1[binIndex] + + P2[binIndex] * F1[binIndex] - + 2 * F1[binIndex] * F2[binIndex] * P2[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])); + const double dI11 = + -(P1[binIndex] * P2[binIndex] - P1[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])); + const double dI10 = + -(P1[binIndex] - P1[binIndex] * P2[binIndex] - + F2[binIndex] * P1[binIndex] + + 2 * F2[binIndex] * P1[binIndex] * P2[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])); + const double factor1 = + (-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - F1[binIndex]); + const double dF1 = + -(ppY[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - + ppY[binIndex] + 2 * ppY[binIndex] * P1[binIndex] + + ppY[binIndex] * F2[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])) + + (ppY[binIndex] * P2[binIndex] * F1[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + + 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + + ppY[binIndex] * P1[binIndex] * P2[binIndex] - + P1[binIndex] * mpY[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] + + P1[binIndex] * mmY[binIndex] * P2[binIndex] - + ppY[binIndex] * F1[binIndex] + + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - + P1[binIndex] * mmY[binIndex] - + P1[binIndex] * mpY[binIndex] * F2[binIndex] + + ppY[binIndex] * F2[binIndex] * P1[binIndex] + + ppY[binIndex] * F1[binIndex] * F2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + + P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) * + (-1 + 2 * P1[binIndex]) / + ((factor1 * factor1) * (-1 + P2[binIndex])); + const double dF2 = + -(-2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + + 2 * P1[binIndex] * mpY[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] - + P1[binIndex] * mpY[binIndex] + ppY[binIndex] * P1[binIndex] + + ppY[binIndex] * F1[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])); + const double factor2 = + (-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - F1[binIndex]); + const double dP1 = + -(-2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + + 2 * mpY[binIndex] * F2[binIndex] * P2[binIndex] + + ppY[binIndex] * P2[binIndex] - mpY[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] + + mmY[binIndex] * P2[binIndex] + + 2 * ppY[binIndex] * F1[binIndex] - mmY[binIndex] - + mpY[binIndex] * F2[binIndex] + ppY[binIndex] * F2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] + + mpY[binIndex] - ppY[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])) + + (ppY[binIndex] * P2[binIndex] * F1[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + + 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + + ppY[binIndex] * P1[binIndex] * P2[binIndex] - + P1[binIndex] * mpY[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] + + P1[binIndex] * mmY[binIndex] * P2[binIndex] - + ppY[binIndex] * F1[binIndex] + + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - + P1[binIndex] * mmY[binIndex] - + P1[binIndex] * mpY[binIndex] * F2[binIndex] + + ppY[binIndex] * F2[binIndex] * P1[binIndex] + + ppY[binIndex] * F1[binIndex] * F2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + + P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) * + (-1 + 2 * F1[binIndex]) / + ((factor2 * factor2) * (-1 + P2[binIndex])); + const double factor3 = (-1 + P2[binIndex]); + const double dP2 = + -(ppY[binIndex] * F1[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + + 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] + + ppY[binIndex] * P1[binIndex] - P1[binIndex] * mpY[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + + P1[binIndex] * mmY[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (-1 + P2[binIndex])) + + (ppY[binIndex] * P2[binIndex] * F1[binIndex] - + 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - + 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + + 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + + ppY[binIndex] * P1[binIndex] * P2[binIndex] - + P1[binIndex] * mpY[binIndex] * P2[binIndex] + + 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * + P2[binIndex] + + P1[binIndex] * mmY[binIndex] * P2[binIndex] - + ppY[binIndex] * F1[binIndex] + + 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - + P1[binIndex] * mmY[binIndex] - + P1[binIndex] * mpY[binIndex] * F2[binIndex] + + ppY[binIndex] * F2[binIndex] * P1[binIndex] + + ppY[binIndex] * F1[binIndex] * F2[binIndex] - + 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + + P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) / + ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - + F1[binIndex]) * + (factor3 * factor3)); + const double e1 = dI00 * ppE[binIndex]; + const double e2 = dI11 * mmE[binIndex]; + const double e3 = dI10 * mpE[binIndex]; + const double e4 = dF1 * F1E[binIndex]; + const double e5 = dF2 * F2E[binIndex]; + const double e6 = dP1 * P1E[binIndex]; + const double e7 = dP2 * P2E[binIndex]; + pmE[binIndex] = std::sqrt(e1 * e1 + e2 * e2 + e3 * e3 + e4 * e4 + + e5 * e5 + e6 * e6 + e7 * e7); + } + } + } + } +}; + +class PolarizationCorrectionWildesTestPerformance : public CxxTest::TestSuite { +public: + void setUp() override { + using namespace Mantid::API; + auto loadWS = + AlgorithmManager::Instance().createUnmanaged("LoadILLReflectometry"); + loadWS->setChild(true); + loadWS->initialize(); + loadWS->setProperty("Filename", "ILL/D17/317370.nxs"); + loadWS->setProperty("OutputWorkspace", "output"); + loadWS->setProperty("XUnit", "TimeOfFlight"); + loadWS->execute(); + m_ws00 = loadWS->getProperty("OutputWorkspace"); + auto groupDetectors = + AlgorithmManager::Instance().createUnmanaged("GroupDetectors"); + groupDetectors->setChild(true); + groupDetectors->initialize(); + groupDetectors->setProperty("InputWorkspace", m_ws00); + groupDetectors->setProperty("OutputWorkspace", "output"); + groupDetectors->setPropertyValue("WorkspaceIndexList", "201, 202, 203"); + groupDetectors->execute(); + m_ws00 = groupDetectors->getProperty("OutputWorkspace"); + auto convertUnits = + AlgorithmManager::Instance().createUnmanaged("ConvertUnits"); + convertUnits->setChild(true); + convertUnits->initialize(); + convertUnits->setProperty("InputWorkspace", m_ws00); + convertUnits->setProperty("OutputWorkspace", "output"); + convertUnits->setProperty("Target", "Wavelength"); + convertUnits->execute(); + m_ws00 = convertUnits->getProperty("OutputWorkspace"); + auto crop = AlgorithmManager::Instance().createUnmanaged("CropWorkspace"); + crop->setChild(true); + crop->initialize(); + crop->setProperty("InputWorkspace", m_ws00); + crop->setProperty("OutputWorkspace", "output"); + crop->setProperty("XMin", 0.); + crop->execute(); + m_ws00 = crop->getProperty("OutputWorkspace"); + AnalysisDataService::Instance().addOrReplace("00", m_ws00); + m_ws01 = m_ws00->clone(); + AnalysisDataService::Instance().addOrReplace("01", m_ws01); + m_ws10 = m_ws00->clone(); + AnalysisDataService::Instance().addOrReplace("10", m_ws10); + m_ws11 = m_ws00->clone(); + AnalysisDataService::Instance().addOrReplace("11", m_ws11); + auto loadEff = AlgorithmManager::Instance().createUnmanaged( + "LoadILLPolarizationFactors"); + loadEff->setChild(true); + loadEff->initialize(); + loadEff->setProperty("Filename", "ILL/D17/PolarizationFactors.txt"); + loadEff->setProperty("OutputWorkspace", "output"); + loadEff->setProperty("WavelengthReference", m_ws00); + loadEff->execute(); + m_effWS = loadEff->getProperty("OutputWorkspace"); + } + + void tearDown() override { + using namespace Mantid::API; + AnalysisDataService::Instance().clear(); + } + + void test_DirectBeamPerformance() { + using namespace Mantid::API; + for (int i = 0; i < 3000; ++i) { + PolarizationCorrectionWildes correction; + correction.setChild(true); + correction.setRethrows(true); + correction.initialize(); + correction.setProperty("InputWorkspaces", "00"); + correction.setProperty("OutputWorkspace", "output"); + correction.setProperty("Flippers", "0"); + correction.setProperty("Efficiencies", m_effWS); + TS_ASSERT_THROWS_NOTHING(correction.execute()) + } + } + + void test_ThreeInputsPerformanceMissing01() { + using namespace Mantid::API; + for (int i = 0; i < 3000; ++i) { + PolarizationCorrectionWildes correction; + correction.setChild(true); + correction.setRethrows(true); + correction.initialize(); + correction.setProperty("InputWorkspaces", "00, 10, 11"); + correction.setProperty("OutputWorkspace", "output"); + correction.setProperty("Flippers", "00, 10, 11"); + correction.setProperty("Efficiencies", m_effWS); + TS_ASSERT_THROWS_NOTHING(correction.execute()) + } + } + + void test_ThreeInputsPerformanceMissing10() { + using namespace Mantid::API; + for (int i = 0; i < 3000; ++i) { + PolarizationCorrectionWildes correction; + correction.setChild(true); + correction.setRethrows(true); + correction.initialize(); + correction.setProperty("InputWorkspaces", "00, 01, 11"); + correction.setProperty("OutputWorkspace", "output"); + correction.setProperty("Flippers", "00, 01, 11"); + correction.setProperty("Efficiencies", m_effWS); + TS_ASSERT_THROWS_NOTHING(correction.execute()) + } + } + + void test_TwoInputsNoAnalyzerPerformance() { + using namespace Mantid::API; + for (int i = 0; i < 3000; ++i) { + PolarizationCorrectionWildes correction; + correction.setChild(true); + correction.setRethrows(true); + correction.initialize(); + correction.setProperty("InputWorkspaces", "00, 11"); + correction.setProperty("OutputWorkspace", "output"); + correction.setProperty("Flippers", "0, 1"); + correction.setProperty("Efficiencies", m_effWS); + TS_ASSERT_THROWS_NOTHING(correction.execute()) + } + } + + void test_TwoInputsPerformance() { + using namespace Mantid::API; + for (int i = 0; i < 3000; ++i) { + PolarizationCorrectionWildes correction; + correction.setChild(true); + correction.setRethrows(true); + correction.initialize(); + correction.setProperty("InputWorkspaces", "00, 11"); + correction.setProperty("OutputWorkspace", "output"); + correction.setProperty("Flippers", "00, 11"); + correction.setProperty("Efficiencies", m_effWS); + TS_ASSERT_THROWS_NOTHING(correction.execute()) + } + } + +private: + Mantid::API::MatrixWorkspace_sptr m_effWS; + Mantid::API::MatrixWorkspace_sptr m_ws00; + Mantid::API::MatrixWorkspace_sptr m_ws01; + Mantid::API::MatrixWorkspace_sptr m_ws10; + Mantid::API::MatrixWorkspace_sptr m_ws11; +}; + +#endif /* MANTID_ALGORITHMS_POLARIZATIONCORRECTIONWILDESTEST_H_ */ diff --git a/Framework/Algorithms/test/PolarizationEfficiencyCorTest.h b/Framework/Algorithms/test/PolarizationEfficiencyCorTest.h index 08999a4f7669142c541cefce5ab2913b0f57854b..fb710a89379bc262db6590570b38e5aef29ca270 100644 --- a/Framework/Algorithms/test/PolarizationEfficiencyCorTest.h +++ b/Framework/Algorithms/test/PolarizationEfficiencyCorTest.h @@ -13,10 +13,19 @@ #include "MantidAPI/WorkspaceGroup.h" #include "MantidDataObjects/Workspace2D.h" #include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidHistogramData/BinEdges.h" +#include "MantidHistogramData/Counts.h" +#include "MantidHistogramData/LinearGenerator.h" + +#include "MantidTestHelpers/WorkspaceCreationHelper.h" #include <Eigen/Dense> -using Mantid::Algorithms::PolarizationEfficiencyCor; +using namespace Mantid::API; +using namespace Mantid::Algorithms; +using namespace Mantid::DataObjects; +using namespace Mantid::HistogramData; +using namespace WorkspaceCreationHelper; class PolarizationEfficiencyCorTest : public CxxTest::TestSuite { public: @@ -29,1962 +38,521 @@ public: delete suite; } - void tearDown() override { - using namespace Mantid::API; - AnalysisDataService::Instance().clear(); - } + void tearDown() override { AnalysisDataService::Instance().clear(); } - void test_Init() { + void test_input_ws_no_inputs() { PolarizationEfficiencyCor alg; - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - } - - void test_IdealCaseFullCorrections() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nBins{3}; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, 4.2 * yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws01 = ws00->clone(); - MatrixWorkspace_sptr ws10 = ws00->clone(); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{{"ws00", "ws01", "ws10", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 4> wsList{{ws00, ws01, ws10, ws11}}; - for (size_t i = 0; i != 4; ++i) { - for (size_t j = 0; j != nHist; ++j) { - wsList[i]->mutableY(j) *= static_cast<double>(i + 1); - wsList[i]->mutableE(j) *= static_cast<double>(i + 1); - } - AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); - } - auto effWS = idealEfficiencies(edges); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + // Error: Input workspaces are missing. Either a workspace group or a list + // of workspace names must be given + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_input_ws_default_group() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) - const std::array<std::string, 4> POL_DIRS{{"++", "+-", "-+", "--"}}; - for (size_t i = 0; i != 4; ++i) { - const std::string wsName = - m_outputWSName + std::string("_") + POL_DIRS[i]; - MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(wsName)); - TS_ASSERT(ws) - TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &xs = ws->x(j); - const auto &ys = ws->y(j); - const auto &es = ws->e(j); - TS_ASSERT_EQUALS(ys.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - const double y = counts[k]; - TS_ASSERT_EQUALS(xs[k], edges[k]) - TS_ASSERT_EQUALS(ys[k], y * static_cast<double>(i + 1)) - TS_ASSERT_EQUALS(es[k], std::sqrt(y) * static_cast<double>(i + 1)) - } - } - } - } - - void test_IdealCaseThreeInputs10Missing() { idealThreeInputsTest("10"); } - - void test_IdealCaseThreeInputs01Missing() { idealThreeInputsTest("01"); } - - void test_IdealCaseTwoInputsWithAnalyzer() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nBins{3}; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, 4.2 * yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{ - std::initializer_list<std::string>{"ws00", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; - for (size_t i = 0; i != nHist; ++i) { - ws11->mutableY(i) *= 2.; - ws11->mutableE(i) *= 2.; - } - AnalysisDataService::Instance().addOrReplace(wsNames.front(), - wsList.front()); - AnalysisDataService::Instance().addOrReplace(wsNames.back(), wsList.back()); - auto effWS = idealEfficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_input_ws_wildes_group() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "00, 11")) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) - const std::array<std::string, 4> POL_DIRS{{"++", "+-", "-+", "--"}}; - for (size_t i = 0; i != 4; ++i) { - const auto &dir = POL_DIRS[i]; - const std::string wsName = m_outputWSName + std::string("_") + dir; - MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(wsName)); - TS_ASSERT(ws) - TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &xs = ws->x(j); - const auto &ys = ws->y(j); - const auto &es = ws->e(j); - TS_ASSERT_EQUALS(ys.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - const double y = counts[k]; - const double expected = [y, &dir]() { - if (dir == "++") { - return y; - } else if (dir == "--") { - return 2. * y; - } else { - return 0.; - } - }(); - const double expectedError = [y, &dir]() { - if (dir == "++") { - return std::sqrt(y); - } else if (dir == "--") { - return 2. * std::sqrt(y); - } else { - return 0.; - } - }(); - TS_ASSERT_EQUALS(xs[k], edges[k]) - TS_ASSERT_EQUALS(ys[k], expected) - TS_ASSERT_EQUALS(es[k], expectedError) - } - } - } - } - - void test_IdealCaseTwoInputsNoAnalyzer() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nBins{3}; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, 4.2 * yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{ - std::initializer_list<std::string>{"ws00", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; - for (size_t i = 0; i != nHist; ++i) { - ws11->mutableY(i) *= 2.; - ws11->mutableE(i) *= 2.; - } - AnalysisDataService::Instance().addOrReplace(wsNames.front(), - wsList.front()); - AnalysisDataService::Instance().addOrReplace(wsNames.back(), wsList.back()); - auto effWS = idealEfficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_input_ws_fredrikze_group() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0, 1")) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 2) - const std::array<std::string, 2> POL_DIRS{{"++", "--"}}; - for (size_t i = 0; i != 2; ++i) { - const auto &dir = POL_DIRS[i]; - const std::string wsName = m_outputWSName + std::string("_") + dir; - MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(wsName)); - TS_ASSERT(ws) - TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &xs = ws->x(j); - const auto &ys = ws->y(j); - const auto &es = ws->e(j); - TS_ASSERT_EQUALS(ys.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - const double y = counts[k]; - TS_ASSERT_EQUALS(xs[k], edges[k]) - TS_ASSERT_EQUALS(ys[k], y * static_cast<double>(i + 1)) - TS_ASSERT_EQUALS(es[k], std::sqrt(y) * static_cast<double>(i + 1)) - } - } - } - } - - void test_IdealCaseDirectBeamCorrections() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nBins{3}; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, 4.2 * yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - const std::vector<std::string> wsNames{{"ws00"}}; - AnalysisDataService::Instance().addOrReplace(wsNames.front(), ws00); - auto effWS = idealEfficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_input_ws_wildes_wrong_input_size() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0")) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 1) - MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_++"))); - TS_ASSERT(ws) - TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) - for (size_t i = 0; i != nHist; ++i) { - const auto &xs = ws->x(i); - const auto &ys = ws->y(i); - const auto &es = ws->e(i); - TS_ASSERT_EQUALS(ys.size(), nBins) - for (size_t j = 0; j != nBins; ++j) { - const double y = counts[j]; - TS_ASSERT_EQUALS(xs[j], edges[j]) - TS_ASSERT_EQUALS(ys[j], y) - TS_ASSERT_EQUALS(es[j], std::sqrt(y)) - } - } - } - - void test_FullCorrections() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws01 = ws00->clone(); - MatrixWorkspace_sptr ws10 = ws00->clone(); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{{"ws00", "ws01", "ws10", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 4> wsList{{ws00, ws01, ws10, ws11}}; - for (size_t i = 0; i != 4; ++i) { - for (size_t j = 0; j != nHist; ++j) { - wsList[i]->mutableY(j) *= static_cast<double>(i + 1); - wsList[i]->mutableE(j) *= static_cast<double>(i + 1); - } - AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); - } - auto effWS = efficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(2)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + // Error: Some invalid Properties found + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); + } + void test_input_ws_fredrikze_wrong_input_size() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) - fullFourInputsResultsCheck(outputWS, ws00, ws01, ws10, ws11, effWS); - } - - void test_ThreeInputsWithMissing01FlipperConfiguration() { - threeInputsTest("01"); - } - - void test_ThreeInputsWithMissing10FlipperConfiguration() { - threeInputsTest("10"); - } - - void test_TwoInputsWithAnalyzer() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nHist{2}; - constexpr size_t nBins{3}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws01 = nullptr; - MatrixWorkspace_sptr ws10 = nullptr; - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{ - std::initializer_list<std::string>{"ws00", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; - for (size_t i = 0; i != 2; ++i) { - for (size_t j = 0; j != nHist; ++j) { - wsList[i]->mutableY(j) *= static_cast<double>(i + 1); - wsList[i]->mutableE(j) *= static_cast<double>(i + 1); - } - AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); - } - auto effWS = efficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(2)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + // Error: For PA analysis, input group must have 4 periods + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_input_ws_wildes_list() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", "00, 11")) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) - solveMissingIntensities(ws00, ws01, ws10, ws11, effWS); - using namespace Mantid::API; - const double F1 = effWS->y(0).front(); - const double F1e = effWS->e(0).front(); - const double F2 = effWS->y(1).front(); - const double F2e = effWS->e(1).front(); - const double P1 = effWS->y(2).front(); - const double P1e = effWS->e(2).front(); - const double P2 = effWS->y(3).front(); - const double P2e = effWS->e(3).front(); - const Eigen::Vector4d y{ws00->y(0).front(), ws01->y(0).front(), - ws10->y(0).front(), ws11->y(0).front()}; - const auto expected = correction(y, F1, F2, P1, P2); - const Eigen::Vector4d e{ws00->e(0).front(), ws01->e(0).front(), - ws10->e(0).front(), ws11->e(0).front()}; - const auto expectedError = error(y, e, F1, F1e, F2, F2e, P1, P1e, P2, P2e); - MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_++"))); - MatrixWorkspace_sptr pmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_+-"))); - MatrixWorkspace_sptr mpWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_-+"))); - MatrixWorkspace_sptr mmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_--"))); - TS_ASSERT(ppWS) - TS_ASSERT(pmWS) - TS_ASSERT(mpWS) - TS_ASSERT(mmWS) - TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) - TS_ASSERT_EQUALS(pmWS->getNumberHistograms(), nHist) - TS_ASSERT_EQUALS(mpWS->getNumberHistograms(), nHist) - TS_ASSERT_EQUALS(mmWS->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &ppX = ppWS->x(j); - const auto &ppY = ppWS->y(j); - const auto &ppE = ppWS->e(j); - const auto &pmX = pmWS->x(j); - const auto &pmY = pmWS->y(j); - const auto &pmE = pmWS->e(j); - const auto &mpX = mpWS->x(j); - const auto &mpY = mpWS->y(j); - const auto &mpE = mpWS->e(j); - const auto &mmX = mmWS->x(j); - const auto &mmY = mmWS->y(j); - const auto &mmE = mmWS->e(j); - TS_ASSERT_EQUALS(ppY.size(), nBins) - TS_ASSERT_EQUALS(pmY.size(), nBins) - TS_ASSERT_EQUALS(mpY.size(), nBins) - TS_ASSERT_EQUALS(mmY.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - TS_ASSERT_EQUALS(ppX[k], edges[k]) - TS_ASSERT_EQUALS(pmX[k], edges[k]) - TS_ASSERT_EQUALS(mpX[k], edges[k]) - TS_ASSERT_EQUALS(mmX[k], edges[k]) - TS_ASSERT_DELTA(ppY[k], expected[0], 1e-12) - TS_ASSERT_DELTA(pmY[k], expected[1], 1e-12) - TS_ASSERT_DELTA(mpY[k], expected[2], 1e-12) - TS_ASSERT_DELTA(mmY[k], expected[3], 1e-12) - // This test constructs the expected missing I01 and I10 intensities - // slightly different from what the algorithm does: I10 is solved - // first and then I01 is solved using all I00, I10 and I11. This - // results in slightly larger errors estimates for I01 and thus for - // the final corrected expected intensities. - TS_ASSERT_DELTA(ppE[k], expectedError[0], 1e-6) - TS_ASSERT_LESS_THAN(ppE[k], expectedError[0]) - TS_ASSERT_DELTA(pmE[k], expectedError[1], 1e-2) - TS_ASSERT_LESS_THAN(pmE[k], expectedError[1]) - TS_ASSERT_DELTA(mpE[k], expectedError[2], 1e-7) - TS_ASSERT_LESS_THAN(mpE[k], expectedError[2]) - TS_ASSERT_DELTA(mmE[k], expectedError[3], 1e-5) - TS_ASSERT_LESS_THAN(mmE[k], expectedError[3]) - } - } - } - - void test_TwoInputsWithoutAnalyzer() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nHist{2}; - constexpr size_t nBins{3}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{ - std::initializer_list<std::string>{"ws00", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 2> wsList{{ws00, ws11}}; - for (size_t i = 0; i != 2; ++i) { - for (size_t j = 0; j != nHist; ++j) { - wsList[i]->mutableY(j) *= static_cast<double>(i + 1); - wsList[i]->mutableE(j) *= static_cast<double>(i + 1); - } - AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); - } - auto effWS = efficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_input_ws_frederikze_needs_group() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", "0, 1")) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 2) - const double F1 = effWS->y(0).front(); - const double F1e = effWS->e(0).front(); - const double P1 = effWS->y(2).front(); - const double P1e = effWS->e(2).front(); - const Eigen::Vector2d y{ws00->y(0).front(), ws11->y(0).front()}; - const auto expected = correctionWithoutAnalyzer(y, F1, P1); - const Eigen::Vector2d e{ws00->e(0).front(), ws11->e(0).front()}; - const auto expectedError = errorWithoutAnalyzer(y, e, F1, F1e, P1, P1e); - MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_++"))); - MatrixWorkspace_sptr mmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_--"))); - TS_ASSERT(ppWS) - TS_ASSERT(mmWS) - TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) - TS_ASSERT_EQUALS(mmWS->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &ppX = ppWS->x(j); - const auto &ppY = ppWS->y(j); - const auto &ppE = ppWS->e(j); - const auto &mmX = mmWS->x(j); - const auto &mmY = mmWS->y(j); - const auto &mmE = mmWS->e(j); - TS_ASSERT_EQUALS(ppY.size(), nBins) - TS_ASSERT_EQUALS(mmY.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - TS_ASSERT_EQUALS(ppX[k], edges[k]) - TS_ASSERT_EQUALS(mmX[k], edges[k]) - TS_ASSERT_DELTA(ppY[k], expected[0], 1e-12) - TS_ASSERT_DELTA(mmY[k], expected[1], 1e-12) - TS_ASSERT_DELTA(ppE[k], expectedError[0], 1e-12) - TS_ASSERT_DELTA(mmE[k], expectedError[1], 1e-12) - } - } - } - - void test_directBeamOnlyInput() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nHist{2}; - constexpr size_t nBins{3}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - const std::string wsName{"ws00"}; - AnalysisDataService::Instance().addOrReplace(wsName, ws00); - auto effWS = efficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + // Error: Input workspaces are required to be in a workspace group + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_input_ws_cannot_be_both() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaces", wsName)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", "0")) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 1) - const auto P1 = effWS->y(2).front(); - const auto P1e = effWS->e(2).front(); - const auto P2 = effWS->y(3).front(); - const auto P2e = effWS->e(3).front(); - const double y{ws00->y(0).front()}; - const auto inverted = 1. / (1. - P2 - P1 + 2. * P1 * P2); - const auto expected = inverted * y; - const double e{ws00->e(0).front()}; - const auto errorP1 = P1e * y * (2. * P1 - 1.) * inverted * inverted; - const auto errorP2 = P2e * y * (2. * P2 - 1.) * inverted * inverted; - const auto errorY = e * e * inverted * inverted; - const auto expectedError = - std::sqrt(errorP1 * errorP1 + errorP2 * errorP2 + errorY); - MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_++"))); - TS_ASSERT(ppWS) - TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &ppX = ppWS->x(j); - const auto &ppY = ppWS->y(j); - const auto &ppE = ppWS->e(j); - TS_ASSERT_EQUALS(ppY.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - TS_ASSERT_EQUALS(ppX[k], edges[k]) - TS_ASSERT_DELTA(ppY[k], expected, 1e-12) - TS_ASSERT_DELTA(ppE[k], expectedError, 1e-12) - } - } - } - - void test_FailureWhenEfficiencyHistogramIsMissing() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - Counts counts{0., 0., 0.}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(1, Histogram(edges, counts)); - const std::string wsName{"ws00"}; - AnalysisDataService::Instance().addOrReplace(wsName, ws00); - auto effWS = idealEfficiencies(edges); - // Rename F1 to something else. - auto axis = make_unique<TextAxis>(4); - axis->setLabel(0, "__wrong_histogram_label"); - axis->setLabel(1, "F2"); - axis->setLabel(2, "P1"); - axis->setLabel(3, "P2"); - effWS->replaceAxis(1, axis.release()); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + // Error: Input workspaces must be given either as a workspace group or a + // list of names + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_input_ws_wildes_wrong_size() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaces", wsName)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0")) - TS_ASSERT_THROWS(alg.execute(), std::runtime_error) - TS_ASSERT(!alg.isExecuted()) + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(2)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + // Error: Some invalid Properties found + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); } - void test_FailureWhenEfficiencyXDataMismatches() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - Counts counts{0., 0., 0.}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(1, Histogram(edges, counts)); - const std::string wsName{"ws00"}; - AnalysisDataService::Instance().addOrReplace(wsName, ws00); - auto effWS = idealEfficiencies(edges); - // Change a bin edge of one of the histograms. - auto &xs = effWS->mutableX(0); - xs[xs.size() / 2] *= 1.01; + void test_efficiencies_fredrikze_wrong_efficiencies() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("InputWorkspaces", wsName)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", "0")) - TS_ASSERT_THROWS(alg.execute(), std::runtime_error) - TS_ASSERT(!alg.isExecuted()) - } - - void test_FailureWhenNumberOfHistogramsInInputWorkspacesMismatch() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - Counts counts{0., 0., 0.}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws01 = ws00->clone(); - MatrixWorkspace_sptr ws10 = - create<Workspace2D>(nHist + 1, Histogram(edges, counts)); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{{"ws00", "ws01", "ws10", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 4> wsList{{ws00, ws01, ws10, ws11}}; - for (size_t i = 0; i != 4; ++i) { - AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); - } - auto effWS = idealEfficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + // Error: Efficiencey property not found: Rho; + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_efficiencies_wildes_wrong_efficiencies() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - TS_ASSERT_THROWS(alg.execute(), std::runtime_error) - TS_ASSERT(!alg.isExecuted()) - } - - void test_FailureWhenAnInputWorkspaceIsMissing() { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - Counts counts{0., 0., 0.}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws01 = ws00->clone(); - MatrixWorkspace_sptr ws11 = ws00->clone(); - AnalysisDataService::Instance().addOrReplace("ws00", ws00); - AnalysisDataService::Instance().addOrReplace("ws01", ws01); - AnalysisDataService::Instance().addOrReplace("ws11", ws11); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + // Error: Some invalid Properties found + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); + } + void test_flippers_full() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS( - alg.setPropertyValue("InputWorkspaces", "ws00, ws01, ws10, ws11"), - std::invalid_argument) - } - -private: - const std::string m_outputWSName{"output"}; - - Mantid::API::MatrixWorkspace_sptr - efficiencies(const Mantid::HistogramData::BinEdges &edges) { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - const auto nBins = edges.size() - 1; - constexpr size_t nHist{4}; - Counts counts(nBins, 0.0); - MatrixWorkspace_sptr ws = - create<Workspace2D>(nHist, Histogram(edges, counts)); - ws->mutableY(0) = 0.95; - ws->mutableE(0) = 0.01; - ws->mutableY(1) = 0.92; - ws->mutableE(1) = 0.02; - ws->mutableY(2) = 0.05; - ws->mutableE(2) = 0.015; - ws->mutableY(3) = 0.04; - ws->mutableE(3) = 0.03; - auto axis = make_unique<TextAxis>(4); - axis->setLabel(0, "F1"); - axis->setLabel(1, "F2"); - axis->setLabel(2, "P1"); - axis->setLabel(3, "P2"); - ws->replaceAxis(1, axis.release()); - return ws; - } - - Mantid::API::MatrixWorkspace_sptr - idealEfficiencies(const Mantid::HistogramData::BinEdges &edges) { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - const auto nBins = edges.size() - 1; - constexpr size_t nHist{4}; - Counts counts(nBins, 0.0); - MatrixWorkspace_sptr ws = - create<Workspace2D>(nHist, Histogram(edges, counts)); - ws->mutableY(0) = 1.; - ws->mutableY(1) = 1.; - auto axis = make_unique<TextAxis>(4); - axis->setLabel(0, "F1"); - axis->setLabel(1, "F2"); - axis->setLabel(2, "P1"); - axis->setLabel(3, "P2"); - ws->replaceAxis(1, axis.release()); - return ws; - } - - void idealThreeInputsTest(const std::string &missingFlipperConf) { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nBins{3}; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, 4.2 * yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr wsXX = ws00->clone(); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{{"ws00", "wsXX", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 3> wsList{{ws00, wsXX, ws11}}; - for (size_t i = 0; i != 3; ++i) { - for (size_t j = 0; j != nHist; ++j) { - wsList[i]->mutableY(j) *= static_cast<double>(i + 1); - wsList[i]->mutableE(j) *= static_cast<double>(i + 1); - } - AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); - } - auto effWS = idealEfficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("Flippers", "00, 01, 10, 11"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_flippers_missing_01() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - const std::string presentFlipperConf = - missingFlipperConf == "01" ? "10" : "01"; - const std::string flipperConf = "00, " + presentFlipperConf + ", 11"; - TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Flippers", flipperConf)) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) - const std::array<std::string, 4> POL_DIRS{{"++", "+-", "-+", "--"}}; - for (size_t i = 0; i != 4; ++i) { - const auto &dir = POL_DIRS[i]; - const std::string wsName = m_outputWSName + std::string("_") + dir; - MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(wsName)); - TS_ASSERT(ws) - TS_ASSERT_EQUALS(ws->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &xs = ws->x(j); - const auto &ys = ws->y(j); - const auto &es = ws->e(j); - TS_ASSERT_EQUALS(ys.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - const double y = counts[k]; - const double expected = [y, &dir]() { - if (dir == "++") { - return y; - } else if (dir == "--") { - return 3. * y; - } else { - return 2. * y; - } - }(); - const double expectedError = [y, &dir, &missingFlipperConf]() { - if (dir == "++") { - return std::sqrt(y); - } else if (dir == "--") { - return 3. * std::sqrt(y); - } else { - std::string conf = std::string(dir.front() == '+' ? "0" : "1") + - std::string(dir.back() == '+' ? "0" : "1"); - if (conf != missingFlipperConf) { - return 2. * std::sqrt(y); - } else { - return 0.; - } - } - }(); - TS_ASSERT_EQUALS(xs[k], edges[k]) - TS_ASSERT_EQUALS(ys[k], expected) - TS_ASSERT_EQUALS(es[k], expectedError) - } - } - } - } - - void threeInputsTest(const std::string &missingFlipperConf) { - using namespace Mantid::API; - using namespace Mantid::DataObjects; - using namespace Mantid::HistogramData; - using namespace Mantid::Kernel; - constexpr size_t nHist{2}; - BinEdges edges{0.3, 0.6, 0.9, 1.2}; - const double yVal = 2.3; - Counts counts{yVal, yVal, yVal}; - MatrixWorkspace_sptr ws00 = - create<Workspace2D>(nHist, Histogram(edges, counts)); - MatrixWorkspace_sptr ws01 = - missingFlipperConf == "01" ? nullptr : ws00->clone(); - MatrixWorkspace_sptr ws10 = - missingFlipperConf == "10" ? nullptr : ws00->clone(); - MatrixWorkspace_sptr ws11 = ws00->clone(); - const std::vector<std::string> wsNames{{"ws00", "wsXX", "ws11"}}; - const std::array<MatrixWorkspace_sptr, 3> wsList{ - {ws00, ws01 != nullptr ? ws01 : ws10, ws11}}; - for (size_t i = 0; i != 3; ++i) { - for (size_t j = 0; j != nHist; ++j) { - wsList[i]->mutableY(j) *= static_cast<double>(i + 1); - wsList[i]->mutableE(j) *= static_cast<double>(i + 1); - } - AnalysisDataService::Instance().addOrReplace(wsNames[i], wsList[i]); - } - auto effWS = efficiencies(edges); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(3)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("Flippers", "00, 10, 11"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_flippers_missing_10() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(3)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("Flippers", "00, 01, 11"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_flippers_missing_0110() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(2)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("Flippers", "00, 11"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_flippers_no_analyser() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(2)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("Flippers", "0, 1"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 2); + } + void test_flippers_direct_beam() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(1)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("Flippers", "0"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 1); + } + void test_flippers_wrong_flippers() { PolarizationEfficiencyCor alg; - alg.setChild(true); alg.setRethrows(true); - TS_ASSERT_THROWS_NOTHING(alg.initialize()) - TS_ASSERT(alg.isInitialized()) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspaces", wsNames)) - TS_ASSERT_THROWS_NOTHING( - alg.setPropertyValue("OutputWorkspace", m_outputWSName)) - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Efficiencies", effWS)) - const std::string presentFlipperConf = - missingFlipperConf == "01" ? "10" : "01"; - const std::string flipperConf = "00, " + presentFlipperConf + ", 11"; - TS_ASSERT_THROWS_NOTHING(alg.setProperty("Flippers", flipperConf)) - TS_ASSERT_THROWS_NOTHING(alg.execute()) - TS_ASSERT(alg.isExecuted()) - WorkspaceGroup_sptr outputWS = alg.getProperty("OutputWorkspace"); - TS_ASSERT(outputWS) - TS_ASSERT_EQUALS(outputWS->getNumberOfEntries(), 4) - solveMissingIntensity(ws00, ws01, ws10, ws11, effWS); - fullFourInputsResultsCheck(outputWS, ws00, ws01, ws10, ws11, effWS); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("Flippers", "00, 10, 11"); + // Error: Some invalid Properties found + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); + } + void test_flippers_wildes_no_pnr() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("PolarizationAnalysis", "PNR"); + // Error: Property PolarizationAnalysis canot be used with the Wildes + // method + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_flippers_wildes_no_pa() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("Wildes")); + alg.setProperty("PolarizationAnalysis", "PA"); + // Error: Property PolarizationAnalysis canot be used with the Wildes + // method + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_polarization_analysis_pnr() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(2)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + alg.setProperty("PolarizationAnalysis", "PNR"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 2); + } + void test_polarization_analysis_pa() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + alg.setProperty("PolarizationAnalysis", "PA"); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + void test_polarization_analysis_wrong_group_size() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + alg.setProperty("PolarizationAnalysis", "PNR"); + // Error: For PNR analysis, input group must have 2 periods + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + void test_polarization_analysis_no_flippers() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaceGroup", createWorkspaceGroup(4)); + alg.setProperty("CorrectionMethod", "Fredrikze"); + alg.setProperty("Efficiencies", createEfficiencies("Fredrikze")); + alg.setProperty("Flippers", "00, 01, 10, 11"); + // Error: Property Flippers canot be used with the Fredrikze method + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); } - void fullFourInputsResultsCheck(Mantid::API::WorkspaceGroup_sptr &outputWS, - Mantid::API::MatrixWorkspace_sptr &ws00, - Mantid::API::MatrixWorkspace_sptr &ws01, - Mantid::API::MatrixWorkspace_sptr &ws10, - Mantid::API::MatrixWorkspace_sptr &ws11, - Mantid::API::MatrixWorkspace_sptr &effWS) { - using namespace Mantid::API; - const auto nHist = ws00->getNumberHistograms(); - const auto nBins = ws00->y(0).size(); - const auto edges = ws00->binEdges(0); - const double F1 = effWS->y(0).front(); - const double F1e = effWS->e(0).front(); - const double F2 = effWS->y(1).front(); - const double F2e = effWS->e(1).front(); - const double P1 = effWS->y(2).front(); - const double P1e = effWS->e(2).front(); - const double P2 = effWS->y(3).front(); - const double P2e = effWS->e(3).front(); - const Eigen::Vector4d y{ws00->y(0).front(), ws01->y(0).front(), - ws10->y(0).front(), ws11->y(0).front()}; - const auto expected = correction(y, F1, F2, P1, P2); - const Eigen::Vector4d e{ws00->e(0).front(), ws01->e(0).front(), - ws10->e(0).front(), ws11->e(0).front()}; - const auto expectedError = error(y, e, F1, F1e, F2, F2e, P1, P1e, P2, P2e); - MatrixWorkspace_sptr ppWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_++"))); - MatrixWorkspace_sptr pmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_+-"))); - MatrixWorkspace_sptr mpWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_-+"))); - MatrixWorkspace_sptr mmWS = boost::dynamic_pointer_cast<MatrixWorkspace>( - outputWS->getItem(m_outputWSName + std::string("_--"))); - TS_ASSERT(ppWS) - TS_ASSERT(pmWS) - TS_ASSERT(mpWS) - TS_ASSERT(mmWS) - TS_ASSERT_EQUALS(ppWS->getNumberHistograms(), nHist) - TS_ASSERT_EQUALS(pmWS->getNumberHistograms(), nHist) - TS_ASSERT_EQUALS(mpWS->getNumberHistograms(), nHist) - TS_ASSERT_EQUALS(mmWS->getNumberHistograms(), nHist) - for (size_t j = 0; j != nHist; ++j) { - const auto &ppX = ppWS->x(j); - const auto &ppY = ppWS->y(j); - const auto &ppE = ppWS->e(j); - const auto &pmX = pmWS->x(j); - const auto &pmY = pmWS->y(j); - const auto &pmE = pmWS->e(j); - const auto &mpX = mpWS->x(j); - const auto &mpY = mpWS->y(j); - const auto &mpE = mpWS->e(j); - const auto &mmX = mmWS->x(j); - const auto &mmY = mmWS->y(j); - const auto &mmE = mmWS->e(j); - TS_ASSERT_EQUALS(ppY.size(), nBins) - TS_ASSERT_EQUALS(pmY.size(), nBins) - TS_ASSERT_EQUALS(mpY.size(), nBins) - TS_ASSERT_EQUALS(mmY.size(), nBins) - for (size_t k = 0; k != nBins; ++k) { - TS_ASSERT_EQUALS(ppX[k], edges[k]) - TS_ASSERT_EQUALS(pmX[k], edges[k]) - TS_ASSERT_EQUALS(mpX[k], edges[k]) - TS_ASSERT_EQUALS(mmX[k], edges[k]) - TS_ASSERT_DELTA(ppY[k], expected[0], 1e-12) - TS_ASSERT_DELTA(pmY[k], expected[1], 1e-12) - TS_ASSERT_DELTA(mpY[k], expected[2], 1e-12) - TS_ASSERT_DELTA(mmY[k], expected[3], 1e-12) - TS_ASSERT_DELTA(ppE[k], expectedError[0], 1e-12) - TS_ASSERT_DELTA(pmE[k], expectedError[1], 1e-12) - TS_ASSERT_DELTA(mpE[k], expectedError[2], 1e-12) - TS_ASSERT_DELTA(mmE[k], expectedError[3], 1e-12) - } + void test_histo() { + PolarizationEfficiencyCor alg; + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", createWorkspacesInADS(4)); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("histo")); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + } + + void test_points() { + PolarizationEfficiencyCor alg; + auto const inputs = createWorkspacesInADS(4); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", inputs); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("points")); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + + for (size_t i = 0; i < out->size(); ++i) { + auto ws = AnalysisDataService::Instance().retrieve(inputs[i]); + auto checkAlg = + AlgorithmManager::Instance().createUnmanaged("CompareWorkspaces"); + checkAlg->initialize(); + checkAlg->setChild(true); + checkAlg->setProperty("Workspace1", ws); + checkAlg->setProperty("Workspace2", out->getItem(i)); + checkAlg->setProperty("Tolerance", 3e-16); + checkAlg->execute(); + TS_ASSERT(checkAlg->getProperty("Result")); } } - Eigen::Matrix4d invertedF1(const double f1) { - Eigen::Matrix4d m; - m << f1, 0., 0., 0., 0., f1, 0., 0., f1 - 1., 0., 1., 0., 0., f1 - 1., 0., - 1.; - m *= 1. / f1; - return m; - } - - Eigen::Matrix4d invertedF1Derivative(const double f1) { - Eigen::Matrix4d m; - m << 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., -1., 0., 0., 1., 0., -1.; - m *= 1. / (f1 * f1); - return m; - } - - Eigen::Matrix4d invertedF2(const double f2) { - Eigen::Matrix4d m; - m << f2, 0., 0., 0., f2 - 1., 1., 0., 0., 0., 0., f2, 0., 0., 0., f2 - 1., - 1.; - m *= 1. / f2; - return m; - } - - Eigen::Matrix4d invertedF2Derivative(const double f2) { - Eigen::Matrix4d m; - m << 0., 0., 0., 0., 1., -1., 0., 0., 0., 0., 0., 0., 0., 0., 1., -1.; - m *= 1. / (f2 * f2); - return m; - } - - Eigen::Matrix4d invertedP1(const double p1) { - Eigen::Matrix4d m; - m << p1 - 1., 0., p1, 0., 0., p1 - 1., 0., p1, p1, 0., p1 - 1., 0., 0., p1, - 0., p1 - 1.; - m *= 1. / (2. * p1 - 1.); - return m; - } - Eigen::Matrix4d invertedP1Derivative(const double p1) { - Eigen::Matrix4d m; - m << 1., 0., -1., 0., 0., 1., 0., -1., -1., 0., 1., 0., 0., -1., 0., 1.; - m *= 1. / (2. * p1 - 1.) / (2. * p1 - 1.); - return m; - } - - Eigen::Matrix4d invertedP2(const double p2) { - Eigen::Matrix4d m; - m << p2 - 1., p2, 0., 0., p2, p2 - 1., 0., 0., 0., 0., p2 - 1., p2, 0., 0., - p2, p2 - 1.; - m *= 1. / (2. * p2 - 1.); - return m; - } - - Eigen::Matrix4d invertedP2Derivative(const double p2) { - Eigen::Matrix4d m; - m << 1., -1., 0., 0., -1., 1., 0., 0., 0., 0., 1., -1., 0., 0., -1., 1.; - m *= 1. / (2. * p2 - 1.) / (2. * p2 - 1.); - return m; - } - - Eigen::Vector4d correction(const Eigen::Vector4d &y, const double f1, - const double f2, const double p1, - const double p2) { - const Eigen::Matrix4d F1 = invertedF1(f1); - const Eigen::Matrix4d F2 = invertedF2(f2); - const Eigen::Matrix4d P1 = invertedP1(p1); - const Eigen::Matrix4d P2 = invertedP2(p2); - const Eigen::Matrix4d inverted = (P2 * P1 * F2 * F1).matrix(); - return (inverted * y).matrix(); - } - - Eigen::Vector4d error(const Eigen::Vector4d &y, const Eigen::Vector4d &e, - const double f1, const double f1e, const double f2, - const double f2e, const double p1, const double p1e, - const double p2, const double p2e) { - const Eigen::Matrix4d F1 = invertedF1(f1); - const Eigen::Matrix4d dF1 = f1e * invertedF1Derivative(f1); - const Eigen::Matrix4d F2 = invertedF2(f2); - const Eigen::Matrix4d dF2 = f2e * invertedF2Derivative(f2); - const Eigen::Matrix4d P1 = invertedP1(p1); - const Eigen::Matrix4d dP1 = p1e * invertedP1Derivative(p1); - const Eigen::Matrix4d P2 = invertedP2(p2); - const Eigen::Matrix4d dP2 = p2e * invertedP2Derivative(p2); - const auto p2Error = (dP2 * P1 * F2 * F1 * y).array(); - const auto p1Error = (P2 * dP1 * F2 * F1 * y).array(); - const auto f2Error = (P2 * P1 * dF2 * F1 * y).array(); - const auto f1Error = (P2 * P1 * F2 * dF1 * y).array(); - const auto inverted = (P2 * P1 * F2 * F1).array(); - const auto yError = ((inverted * inverted).matrix() * - (e.array() * e.array()).matrix()).array(); - return (p2Error * p2Error + p1Error * p1Error + f2Error * f2Error + - f1Error * f1Error + yError) - .sqrt() - .matrix(); - } - - Eigen::Vector2d correctionWithoutAnalyzer(const Eigen::Vector2d &y, - const double f1, const double p1) { - Eigen::Matrix2d F1; - F1 << f1, 0., f1 - 1., 1.; - F1 *= 1. / f1; - Eigen::Matrix2d P1; - P1 << p1 - 1., p1, p1, p1 - 1.; - P1 *= 1. / (2. * p1 - 1.); - const Eigen::Matrix2d inverted = (P1 * F1).matrix(); - return static_cast<Eigen::Vector2d>(inverted * y); - } - - Eigen::Vector2d errorWithoutAnalyzer(const Eigen::Vector2d &y, - const Eigen::Vector2d &e, - const double f1, const double f1e, - const double p1, const double p1e) { - Eigen::Matrix2d F1; - F1 << f1, 0, f1 - 1., 1.; - F1 *= 1. / f1; - Eigen::Matrix2d dF1; - dF1 << 0., 0., 1., -1.; - dF1 *= f1e / (f1 * f1); - Eigen::Matrix2d P1; - P1 << p1 - 1., p1, p1, p1 - 1.; - P1 *= 1. / (2. * p1 - 1.); - Eigen::Matrix2d dP1; - dP1 << 1., -1., -1., 1.; - dP1 *= p1e / ((2. * p1 - 1.) * (2. * p1 - 1.)); - const auto p1Error = (dP1 * F1 * y).array(); - const auto f1Error = (P1 * dF1 * y).array(); - const auto inverted = (P1 * F1).array(); - const auto yError = ((inverted * inverted).matrix() * - (e.array() * e.array()).matrix()).array(); - return (p1Error * p1Error + f1Error * f1Error + yError).sqrt().matrix(); - } - - void solveMissingIntensity(const Mantid::API::MatrixWorkspace_sptr &ppWS, - Mantid::API::MatrixWorkspace_sptr &pmWS, - Mantid::API::MatrixWorkspace_sptr &mpWS, - const Mantid::API::MatrixWorkspace_sptr &mmWS, - const Mantid::API::MatrixWorkspace_sptr &effWS) { - const auto &F1 = effWS->y(0); - const auto &F2 = effWS->y(1); - const auto &P1 = effWS->y(2); - const auto &P2 = effWS->y(3); - if (!pmWS) { - pmWS = mpWS->clone(); - for (size_t wsIndex = 0; wsIndex != pmWS->getNumberHistograms(); - ++wsIndex) { - const auto &ppY = ppWS->y(wsIndex); - auto &pmY = pmWS->mutableY(wsIndex); - auto &pmE = pmWS->mutableE(wsIndex); - const auto &mpY = mpWS->y(wsIndex); - const auto &mmY = mmWS->y(wsIndex); - for (size_t binIndex = 0; binIndex != mpY.size(); ++binIndex) { - pmY[binIndex] = - -(2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - - P2[binIndex] * mmY[binIndex] - - 2 * mpY[binIndex] * F2[binIndex] * P2[binIndex] + - mpY[binIndex] * P2[binIndex] - ppY[binIndex] * P2[binIndex] + - P1[binIndex] * mmY[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + - ppY[binIndex] * P1[binIndex] - P1[binIndex] * mpY[binIndex] + - ppY[binIndex] * F1[binIndex] + mpY[binIndex] * F2[binIndex] - - ppY[binIndex] * F2[binIndex]) / - (P2[binIndex] - P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]); - // Error propagation is not implemented in the algorithm. - pmE[binIndex] = 0.; - } - } - } else { - mpWS = pmWS->clone(); - for (size_t wsIndex = 0; wsIndex != mpWS->getNumberHistograms(); - ++wsIndex) { - const auto &ppY = ppWS->y(wsIndex); - const auto &pmY = pmWS->y(wsIndex); - auto &mpY = mpWS->mutableY(wsIndex); - auto &mpE = mpWS->mutableE(wsIndex); - const auto &mmY = mmWS->y(wsIndex); - for (size_t binIndex = 0; binIndex != mpY.size(); ++binIndex) { - mpY[binIndex] = - (-ppY[binIndex] * P2[binIndex] + P2[binIndex] * pmY[binIndex] - - P2[binIndex] * mmY[binIndex] + - 2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - - pmY[binIndex] * P1[binIndex] + P1[binIndex] * mmY[binIndex] + - ppY[binIndex] * P1[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + - 2 * pmY[binIndex] * F1[binIndex] * P1[binIndex] + - ppY[binIndex] * F1[binIndex] - ppY[binIndex] * F2[binIndex] - - pmY[binIndex] * F1[binIndex]) / - (-P2[binIndex] + 2 * F2[binIndex] * P2[binIndex] + P1[binIndex] - - F2[binIndex]); - // Error propagation is not implemented in the algorithm. - mpE[binIndex] = 0.; - } - } + void test_points_short() { + PolarizationEfficiencyCor alg; + auto const inputs = createWorkspacesInADS(4); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("OutputWorkspace", "out"); + alg.setProperty("InputWorkspaces", inputs); + alg.setProperty("CorrectionMethod", "Wildes"); + alg.setProperty("Efficiencies", createEfficiencies("points-short")); + alg.execute(); + WorkspaceGroup_sptr out = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("out"); + TS_ASSERT_EQUALS(out->size(), 4); + + for (size_t i = 0; i < out->size(); ++i) { + auto ws = AnalysisDataService::Instance().retrieve(inputs[i]); + auto checkAlg = + AlgorithmManager::Instance().createUnmanaged("CompareWorkspaces"); + checkAlg->initialize(); + checkAlg->setChild(true); + checkAlg->setProperty("Workspace1", ws); + checkAlg->setProperty("Workspace2", out->getItem(i)); + checkAlg->setProperty("Tolerance", 3e-16); + checkAlg->execute(); + TS_ASSERT(checkAlg->getProperty("Result")); } } - void solveMissingIntensities(const Mantid::API::MatrixWorkspace_sptr &ppWS, - Mantid::API::MatrixWorkspace_sptr &pmWS, - Mantid::API::MatrixWorkspace_sptr &mpWS, - const Mantid::API::MatrixWorkspace_sptr &mmWS, - const Mantid::API::MatrixWorkspace_sptr &effWS) { - const auto &F1 = effWS->y(0); - const auto &F1E = effWS->e(0); - const auto &F2 = effWS->y(1); - const auto &F2E = effWS->e(1); - const auto &P1 = effWS->y(2); - const auto &P1E = effWS->e(2); - const auto &P2 = effWS->y(3); - const auto &P2E = effWS->e(3); - pmWS = ppWS->clone(); - mpWS = ppWS->clone(); - for (size_t wsIndex = 0; wsIndex != ppWS->getNumberHistograms(); - ++wsIndex) { - const auto &ppY = ppWS->y(wsIndex); - const auto &ppE = ppWS->e(wsIndex); - auto &pmY = pmWS->mutableY(wsIndex); - auto &pmE = pmWS->mutableE(wsIndex); - auto &mpY = mpWS->mutableY(wsIndex); - auto &mpE = mpWS->mutableE(wsIndex); - const auto &mmY = mmWS->y(wsIndex); - const auto &mmE = mmWS->e(wsIndex); - for (size_t binIndex = 0; binIndex != mpY.size(); ++binIndex) { - const double P12 = P1[binIndex] * P1[binIndex]; - const double P13 = P1[binIndex] * P12; - const double P14 = P1[binIndex] * P13; - const double P22 = P2[binIndex] * P2[binIndex]; - const double P23 = P2[binIndex] * P22; - const double F12 = F1[binIndex] * F1[binIndex]; - { - mpY[binIndex] = - -(-mmY[binIndex] * P22 * F1[binIndex] + - 2 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * - P2[binIndex] + - 2 * ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] + - 8 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] + - 2 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] - - 8 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * - P1[binIndex] - - 2 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] + - mmY[binIndex] * P2[binIndex] * F1[binIndex] + - ppY[binIndex] * F1[binIndex] * F2[binIndex] - - ppY[binIndex] * F2[binIndex] * P12 + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 + - 4 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] - - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + - ppY[binIndex] * F2[binIndex] * P1[binIndex] - - 4 * ppY[binIndex] * F12 * F2[binIndex] * P12 - - ppY[binIndex] * F12 * F2[binIndex]) / - (-F1[binIndex] * F2[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P22 - - 2 * P22 * F1[binIndex] * P1[binIndex] + - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - - P2[binIndex] * F1[binIndex] + P22 * F1[binIndex] + - F2[binIndex] * P12 - 2 * F2[binIndex] * P12 * P2[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P12 - - F2[binIndex] * P1[binIndex] - - 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); - const double dI00 = - -F2[binIndex] * - (-2 * P2[binIndex] * F1[binIndex] + 2 * P12 * P2[binIndex] + - 8 * P2[binIndex] * F1[binIndex] * P1[binIndex] - - 2 * P1[binIndex] * P2[binIndex] + 2 * P2[binIndex] * F12 - - 8 * P2[binIndex] * F12 * P1[binIndex] - - 8 * P2[binIndex] * F1[binIndex] * P12 + - 8 * P2[binIndex] * F12 * P12 - 4 * F1[binIndex] * P1[binIndex] - - F12 + 4 * F12 * P1[binIndex] + P1[binIndex] + F1[binIndex] - - P12 + 4 * F1[binIndex] * P12 - 4 * F12 * P12) / - (-P2[binIndex] * F1[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * P22 * F1[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P22 - - 2 * F2[binIndex] * P12 * P2[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P12 + - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + - P22 * F1[binIndex] + F2[binIndex] * P12 + - 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - - F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - - 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); - const double dI11 = - -P2[binIndex] * F1[binIndex] * - (1 - 2 * P1[binIndex] - P2[binIndex] + - 2 * P1[binIndex] * P2[binIndex]) / - (-P2[binIndex] * F1[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * P22 * F1[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P22 - - 2 * F2[binIndex] * P12 * P2[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P12 + - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + - P22 * F1[binIndex] + F2[binIndex] * P12 + - 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - - F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - - 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); - const double divisor1 = - (-P2[binIndex] * F1[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * P22 * F1[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P22 - - 2 * F2[binIndex] * P12 * P2[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P12 + - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + - P22 * F1[binIndex] + F2[binIndex] * P12 + - 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - - F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - - 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); - const double dF1 = - -F2[binIndex] * - (-P1[binIndex] * mmY[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P22 - - ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] - - 10 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 - - 8 * ppY[binIndex] * F2[binIndex] * P12 * P22 + - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] - - ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - - 32 * ppY[binIndex] * F12 * F2[binIndex] * P14 * P2[binIndex] + - 32 * ppY[binIndex] * F2[binIndex] * P14 * P2[binIndex] * - F1[binIndex] - - 32 * ppY[binIndex] * F2[binIndex] * P14 * P22 * F1[binIndex] + - 32 * ppY[binIndex] * F12 * F2[binIndex] * P14 * P22 + - 32 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P23 + - 2 * ppY[binIndex] * F2[binIndex] * P14 + - 4 * ppY[binIndex] * P13 * P23 - 4 * P13 * mmY[binIndex] * P23 - - 8 * ppY[binIndex] * F2[binIndex] * P13 * P23 - - 16 * ppY[binIndex] * P23 * F12 * P13 + - 8 * ppY[binIndex] * F12 * F2[binIndex] * P14 - - 8 * ppY[binIndex] * F2[binIndex] * P14 * P2[binIndex] + - 8 * ppY[binIndex] * F2[binIndex] * P14 * P22 - - 8 * ppY[binIndex] * F2[binIndex] * P14 * F1[binIndex] + - 10 * ppY[binIndex] * F2[binIndex] * P13 * P2[binIndex] - - 4 * ppY[binIndex] * F2[binIndex] * P13 * P22 + - 16 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 - - 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P23 + - 12 * ppY[binIndex] * F2[binIndex] * P12 * P23 + - 18 * ppY[binIndex] * P22 * F12 * P1[binIndex] - - 20 * ppY[binIndex] * F12 * F2[binIndex] * P13 - - 36 * ppY[binIndex] * P22 * F12 * P12 + - 24 * ppY[binIndex] * P22 * F12 * P13 - - 6 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] - - 5 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] + - 8 * ppY[binIndex] * F12 * F2[binIndex] * P22 - - 8 * ppY[binIndex] * P2[binIndex] * F12 * P13 + - 12 * ppY[binIndex] * P2[binIndex] * F12 * P12 + - 18 * ppY[binIndex] * F12 * F2[binIndex] * P12 - - 7 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] - - 12 * ppY[binIndex] * P23 * F12 * P1[binIndex] + - 24 * ppY[binIndex] * P23 * F12 * P12 - - 4 * ppY[binIndex] * F12 * F2[binIndex] * P23 - - 3 * ppY[binIndex] * P1[binIndex] * P22 + - ppY[binIndex] * F2[binIndex] * P12 - - 3 * ppY[binIndex] * P12 * P2[binIndex] + - 3 * P12 * mmY[binIndex] * P2[binIndex] - - 9 * P12 * mmY[binIndex] * P22 + 9 * ppY[binIndex] * P12 * P22 + - ppY[binIndex] * P1[binIndex] * P2[binIndex] + - 3 * P1[binIndex] * mmY[binIndex] * P22 - - 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] + - 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P22 + - 40 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * - P2[binIndex] - - 40 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P22 - - 64 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * - P2[binIndex] + - 64 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * P22 + - 34 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * - P1[binIndex] - - 52 * ppY[binIndex] * F12 * F2[binIndex] * P22 * P1[binIndex] - - 84 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] + - 120 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P22 + - 88 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P2[binIndex] - - 112 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P22 + - 24 * ppY[binIndex] * F12 * F2[binIndex] * P23 * P1[binIndex] - - 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P23 + - 2 * ppY[binIndex] * P13 * P2[binIndex] - - 6 * ppY[binIndex] * P13 * P22 - - 3 * ppY[binIndex] * F2[binIndex] * P13 + - 2 * ppY[binIndex] * P1[binIndex] * P23 - - 6 * ppY[binIndex] * P12 * P23 + - ppY[binIndex] * P2[binIndex] * F12 - - 3 * ppY[binIndex] * P22 * F12 + - ppY[binIndex] * F12 * F2[binIndex] + - 2 * ppY[binIndex] * P23 * F12 - - 2 * P13 * mmY[binIndex] * P2[binIndex] + - 6 * P13 * mmY[binIndex] * P22 + 6 * P12 * mmY[binIndex] * P23 - - 2 * P1[binIndex] * mmY[binIndex] * P23) / - (divisor1 * divisor1); - const double divisor2 = - (-P2[binIndex] * F1[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * P22 * F1[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P22 - - 2 * F2[binIndex] * P12 * P2[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P12 + - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + - P22 * F1[binIndex] + F2[binIndex] * P12 + - 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - - F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - - 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); - const double dF2 = - P2[binIndex] * F1[binIndex] * - (3 * P1[binIndex] * mmY[binIndex] * P2[binIndex] - - 12 * ppY[binIndex] * P22 * F1[binIndex] * P1[binIndex] - - 36 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P12 + - 24 * ppY[binIndex] * P22 * F1[binIndex] * P12 + - 18 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + - 12 * ppY[binIndex] * F1[binIndex] * P12 + - 24 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P13 - - 16 * ppY[binIndex] * P22 * F1[binIndex] * P13 + - 12 * ppY[binIndex] * P22 * F12 * P1[binIndex] - - 24 * ppY[binIndex] * P22 * F12 * P12 + - 16 * ppY[binIndex] * P22 * F12 * P13 - - 18 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] - - 24 * ppY[binIndex] * P2[binIndex] * F12 * P13 + - 36 * ppY[binIndex] * P2[binIndex] * F12 * P12 - - 19 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] + - 28 * F1[binIndex] * P12 * mmY[binIndex] * P2[binIndex] - - 12 * F1[binIndex] * P13 * mmY[binIndex] * P2[binIndex] + - 22 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 - - 28 * F1[binIndex] * P12 * mmY[binIndex] * P22 + - 8 * F1[binIndex] * P13 * mmY[binIndex] * P22 - - 8 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P23 + - 8 * F1[binIndex] * P12 * mmY[binIndex] * P23 - - ppY[binIndex] * F12 + 2 * ppY[binIndex] * P13 - - 2 * P13 * mmY[binIndex] - mmY[binIndex] * F1[binIndex] + - 2 * ppY[binIndex] * P1[binIndex] * P22 + - 9 * ppY[binIndex] * P12 * P2[binIndex] - - 9 * P12 * mmY[binIndex] * P2[binIndex] + - 6 * P12 * mmY[binIndex] * P22 - 6 * ppY[binIndex] * P12 * P22 - - 3 * ppY[binIndex] * P1[binIndex] * P2[binIndex] - - 2 * P1[binIndex] * mmY[binIndex] * P22 - - 6 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + - 2 * ppY[binIndex] * P22 * F1[binIndex] - - 3 * ppY[binIndex] * P2[binIndex] * F1[binIndex] - - P1[binIndex] * mmY[binIndex] + ppY[binIndex] * P1[binIndex] - - 3 * ppY[binIndex] * P12 + ppY[binIndex] * F1[binIndex] + - 3 * P12 * mmY[binIndex] - - 6 * ppY[binIndex] * P13 * P2[binIndex] + - 4 * ppY[binIndex] * P13 * P22 + - 3 * ppY[binIndex] * P2[binIndex] * F12 - - 2 * ppY[binIndex] * P22 * F12 + - 5 * F1[binIndex] * P1[binIndex] * mmY[binIndex] + - 6 * ppY[binIndex] * F12 * P1[binIndex] - - 8 * F1[binIndex] * P12 * mmY[binIndex] - - 12 * F12 * P12 * ppY[binIndex] - - 8 * ppY[binIndex] * F1[binIndex] * P13 + - 6 * P13 * mmY[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * P13 * mmY[binIndex] + - 8 * F12 * P13 * ppY[binIndex] - 4 * P13 * mmY[binIndex] * P22 - - 5 * mmY[binIndex] * P22 * F1[binIndex] + - 2 * mmY[binIndex] * P23 * F1[binIndex] + - 4 * mmY[binIndex] * P2[binIndex] * F1[binIndex]) / - (divisor2 * divisor2); - const double divisor3 = - (-P2[binIndex] * F1[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * P22 * F1[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P22 - - 2 * F2[binIndex] * P12 * P2[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P12 + - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + - P22 * F1[binIndex] + F2[binIndex] * P12 + - 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - - F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - - 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); - const double dP1 = - -F1[binIndex] * F2[binIndex] * - (-2 * P1[binIndex] * mmY[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] + - 8 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 24 * ppY[binIndex] * P22 * F1[binIndex] * P1[binIndex] + - 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P22 + - 8 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P12 + - 6 * ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 - - 24 * ppY[binIndex] * P22 * F1[binIndex] * P12 - - 12 * ppY[binIndex] * F2[binIndex] * P12 * P22 - - 8 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - ppY[binIndex] * F2[binIndex] * P2[binIndex] - - 4 * ppY[binIndex] * F2[binIndex] * P22 - - 8 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P23 - - 16 * ppY[binIndex] * P23 * F1[binIndex] * P1[binIndex] - - 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P23 + - 16 * ppY[binIndex] * P23 * F1[binIndex] * P12 + - 8 * ppY[binIndex] * F2[binIndex] * P12 * P23 - - 24 * ppY[binIndex] * P22 * F12 * P1[binIndex] + - 24 * ppY[binIndex] * P22 * F12 * P12 + - 8 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] + - 6 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] - - 12 * ppY[binIndex] * F12 * F2[binIndex] * P22 - - 8 * ppY[binIndex] * P2[binIndex] * F12 * P12 - - 4 * ppY[binIndex] * F12 * F2[binIndex] * P12 + - 4 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] + - 16 * ppY[binIndex] * P23 * F12 * P1[binIndex] - - 16 * ppY[binIndex] * P23 * F12 * P12 + - 8 * ppY[binIndex] * F12 * F2[binIndex] * P23 + - 4 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] - - 4 * F1[binIndex] * P12 * mmY[binIndex] * P2[binIndex] - - 12 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 + - 12 * F1[binIndex] * P12 * mmY[binIndex] * P22 + - 8 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P23 - - 8 * F1[binIndex] * P12 * mmY[binIndex] * P23 + - 2 * mmY[binIndex] * P23 - 2 * ppY[binIndex] * P23 + - 4 * ppY[binIndex] * F2[binIndex] * P23 - - 6 * ppY[binIndex] * P1[binIndex] * P22 - - ppY[binIndex] * F2[binIndex] * P12 - - 2 * ppY[binIndex] * P12 * P2[binIndex] + - 2 * P12 * mmY[binIndex] * P2[binIndex] - - 6 * P12 * mmY[binIndex] * P22 + 6 * ppY[binIndex] * P12 * P22 + - 2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] - - ppY[binIndex] * P2[binIndex] + - 6 * P1[binIndex] * mmY[binIndex] * P22 - - 6 * ppY[binIndex] * P22 * F1[binIndex] + - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + - 3 * ppY[binIndex] * P22 + - 16 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] - - 40 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P22 - - 24 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * - P2[binIndex] + - 48 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P22 + - mmY[binIndex] * P2[binIndex] - 3 * mmY[binIndex] * P22 + - 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P23 - - 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P23 - - 24 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * - P1[binIndex] + - 48 * ppY[binIndex] * F12 * F2[binIndex] * P22 * P1[binIndex] + - 24 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] - - 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P22 - - 32 * ppY[binIndex] * F12 * F2[binIndex] * P23 * P1[binIndex] + - 32 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P23 + - 4 * ppY[binIndex] * P1[binIndex] * P23 + - 4 * ppY[binIndex] * P23 * F1[binIndex] - - 4 * ppY[binIndex] * P12 * P23 - - 2 * ppY[binIndex] * P2[binIndex] * F12 + - 6 * ppY[binIndex] * P22 * F12 - - ppY[binIndex] * F12 * F2[binIndex] - - 4 * ppY[binIndex] * P23 * F12 + 4 * P12 * mmY[binIndex] * P23 - - 4 * P1[binIndex] * mmY[binIndex] * P23 + - 3 * mmY[binIndex] * P22 * F1[binIndex] - - 2 * mmY[binIndex] * P23 * F1[binIndex] - - mmY[binIndex] * P2[binIndex] * F1[binIndex]) / - (divisor3 * divisor3); - const double divisor4 = - (-P2[binIndex] * F1[binIndex] + - 3 * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * P22 * F1[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P22 - - 2 * F2[binIndex] * P12 * P2[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P12 + - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] + - P22 * F1[binIndex] + F2[binIndex] * P12 + - 3 * F1[binIndex] * F2[binIndex] * P1[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] - - F1[binIndex] * F2[binIndex] - F2[binIndex] * P1[binIndex] - - 8 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 4 * F1[binIndex] * F2[binIndex] * P12 * P2[binIndex]); - const double dP2 = - F1[binIndex] * F2[binIndex] * - (-2 * P1[binIndex] * mmY[binIndex] * P2[binIndex] - - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P22 + - 12 * ppY[binIndex] * P22 * F1[binIndex] * P1[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P22 + - 24 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P12 + - 12 * ppY[binIndex] * F2[binIndex] * P12 * P2[binIndex] + - 12 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 - - 24 * ppY[binIndex] * P22 * F1[binIndex] * P12 - - 12 * ppY[binIndex] * F2[binIndex] * P12 * P22 - - 12 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] - - 6 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] - - 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - - 12 * ppY[binIndex] * F1[binIndex] * P12 - - 16 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P13 + - 16 * ppY[binIndex] * P22 * F1[binIndex] * P13 - - 8 * ppY[binIndex] * F2[binIndex] * P13 * P2[binIndex] + - 8 * ppY[binIndex] * F2[binIndex] * P13 * P22 - - 8 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 - - 12 * ppY[binIndex] * P22 * F12 * P1[binIndex] + - 8 * ppY[binIndex] * F12 * F2[binIndex] * P13 + - 24 * ppY[binIndex] * P22 * F12 * P12 - - 16 * ppY[binIndex] * P22 * F12 * P13 + - 12 * ppY[binIndex] * P2[binIndex] * F12 * P1[binIndex] + - 4 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] - - 4 * ppY[binIndex] * F12 * F2[binIndex] * P22 + - 16 * ppY[binIndex] * P2[binIndex] * F12 * P13 - - 24 * ppY[binIndex] * P2[binIndex] * F12 * P12 - - 12 * ppY[binIndex] * F12 * F2[binIndex] * P12 + - 6 * ppY[binIndex] * F12 * F2[binIndex] * P1[binIndex] + - 10 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P2[binIndex] - - 16 * F1[binIndex] * P12 * mmY[binIndex] * P2[binIndex] + - 8 * F1[binIndex] * P13 * mmY[binIndex] * P2[binIndex] - - 6 * F1[binIndex] * P1[binIndex] * mmY[binIndex] * P22 + - 12 * F1[binIndex] * P12 * mmY[binIndex] * P22 - - 8 * F1[binIndex] * P13 * mmY[binIndex] * P22 + - ppY[binIndex] * F12 - 2 * ppY[binIndex] * P13 + - 2 * P13 * mmY[binIndex] + mmY[binIndex] * F1[binIndex] - - 2 * ppY[binIndex] * P1[binIndex] * P22 + - ppY[binIndex] * F2[binIndex] * P1[binIndex] - - 3 * ppY[binIndex] * F2[binIndex] * P12 - - 6 * ppY[binIndex] * P12 * P2[binIndex] + - 6 * P12 * mmY[binIndex] * P2[binIndex] - - 6 * P12 * mmY[binIndex] * P22 + 6 * ppY[binIndex] * P12 * P22 + - 2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] + - ppY[binIndex] * F1[binIndex] * F2[binIndex] + - 2 * P1[binIndex] * mmY[binIndex] * P22 + - 6 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - - 2 * ppY[binIndex] * P22 * F1[binIndex] + - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + - 24 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] - - 24 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P22 - - 48 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * - P2[binIndex] + - 48 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P12 * P22 + - P1[binIndex] * mmY[binIndex] - ppY[binIndex] * P1[binIndex] + - 3 * ppY[binIndex] * P12 - ppY[binIndex] * F1[binIndex] - - 3 * P12 * mmY[binIndex] + - 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * - P2[binIndex] - - 32 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P13 * P22 - - 24 * ppY[binIndex] * F12 * F2[binIndex] * P2[binIndex] * - P1[binIndex] + - 24 * ppY[binIndex] * F12 * F2[binIndex] * P22 * P1[binIndex] + - 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P2[binIndex] - - 48 * ppY[binIndex] * F12 * F2[binIndex] * P12 * P22 - - 32 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P2[binIndex] + - 32 * ppY[binIndex] * F12 * F2[binIndex] * P13 * P22 + - 4 * ppY[binIndex] * P13 * P2[binIndex] - - 4 * ppY[binIndex] * P13 * P22 + - 2 * ppY[binIndex] * F2[binIndex] * P13 - - 2 * ppY[binIndex] * P2[binIndex] * F12 + - 2 * ppY[binIndex] * P22 * F12 - - ppY[binIndex] * F12 * F2[binIndex] - - 5 * F1[binIndex] * P1[binIndex] * mmY[binIndex] - - 6 * ppY[binIndex] * F12 * P1[binIndex] + - 8 * F1[binIndex] * P12 * mmY[binIndex] + - 12 * F12 * P12 * ppY[binIndex] + - 8 * ppY[binIndex] * F1[binIndex] * P13 - - 4 * P13 * mmY[binIndex] * P2[binIndex] - - 4 * F1[binIndex] * P13 * mmY[binIndex] - - 8 * F12 * P13 * ppY[binIndex] + 4 * P13 * mmY[binIndex] * P22 + - mmY[binIndex] * P22 * F1[binIndex] - - 2 * mmY[binIndex] * P2[binIndex] * F1[binIndex]) / - (divisor4 * divisor4); - const double e1 = dI00 * ppE[binIndex]; - const double e2 = dI11 * mmE[binIndex]; - const double e3 = dF1 * F1E[binIndex]; - const double e4 = dF2 * F2E[binIndex]; - const double e5 = dP1 * P1E[binIndex]; - const double e6 = dP2 * P2E[binIndex]; - mpE[binIndex] = std::sqrt(e1 * e1 + e2 * e2 + e3 * e3 + e4 * e4 + - e5 * e5 + e6 * e6); - } - { - pmY[binIndex] = - -(ppY[binIndex] * P2[binIndex] * F1[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + - 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + - ppY[binIndex] * P1[binIndex] * P2[binIndex] - - P1[binIndex] * mpY[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] + - P1[binIndex] * mmY[binIndex] * P2[binIndex] - - ppY[binIndex] * F1[binIndex] + - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - - P1[binIndex] * mmY[binIndex] - - P1[binIndex] * mpY[binIndex] * F2[binIndex] + - ppY[binIndex] * F2[binIndex] * P1[binIndex] + - ppY[binIndex] * F1[binIndex] * F2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + - P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])); - const double dI00 = - -(-P1[binIndex] + P1[binIndex] * P2[binIndex] + - F2[binIndex] * P1[binIndex] - - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex] + - 2 * F1[binIndex] * P1[binIndex] - - 2 * P2[binIndex] * F1[binIndex] * P1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P1[binIndex] + - 4 * F1[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] + - F1[binIndex] * F2[binIndex] - F1[binIndex] + - P2[binIndex] * F1[binIndex] - - 2 * F1[binIndex] * F2[binIndex] * P2[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])); - const double dI11 = - -(P1[binIndex] * P2[binIndex] - P1[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])); - const double dI10 = - -(P1[binIndex] - P1[binIndex] * P2[binIndex] - - F2[binIndex] * P1[binIndex] + - 2 * F2[binIndex] * P1[binIndex] * P2[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])); - const double factor1 = - (-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - F1[binIndex]); - const double dF1 = - -(ppY[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - - ppY[binIndex] + 2 * ppY[binIndex] * P1[binIndex] + - ppY[binIndex] * F2[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])) + - (ppY[binIndex] * P2[binIndex] * F1[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + - 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + - ppY[binIndex] * P1[binIndex] * P2[binIndex] - - P1[binIndex] * mpY[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] + - P1[binIndex] * mmY[binIndex] * P2[binIndex] - - ppY[binIndex] * F1[binIndex] + - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - - P1[binIndex] * mmY[binIndex] - - P1[binIndex] * mpY[binIndex] * F2[binIndex] + - ppY[binIndex] * F2[binIndex] * P1[binIndex] + - ppY[binIndex] * F1[binIndex] * F2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + - P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) * - (-1 + 2 * P1[binIndex]) / - ((factor1 * factor1) * (-1 + P2[binIndex])); - const double dF2 = - -(-2 * ppY[binIndex] * P1[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + - 2 * P1[binIndex] * mpY[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] - - P1[binIndex] * mpY[binIndex] + ppY[binIndex] * P1[binIndex] + - ppY[binIndex] * F1[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])); - const double factor2 = - (-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - F1[binIndex]); - const double dP1 = - -(-2 * ppY[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] + - 2 * mpY[binIndex] * F2[binIndex] * P2[binIndex] + - ppY[binIndex] * P2[binIndex] - mpY[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] + - mmY[binIndex] * P2[binIndex] + - 2 * ppY[binIndex] * F1[binIndex] - mmY[binIndex] - - mpY[binIndex] * F2[binIndex] + ppY[binIndex] * F2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] + - mpY[binIndex] - ppY[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])) + - (ppY[binIndex] * P2[binIndex] * F1[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + - 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + - ppY[binIndex] * P1[binIndex] * P2[binIndex] - - P1[binIndex] * mpY[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] + - P1[binIndex] * mmY[binIndex] * P2[binIndex] - - ppY[binIndex] * F1[binIndex] + - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - - P1[binIndex] * mmY[binIndex] - - P1[binIndex] * mpY[binIndex] * F2[binIndex] + - ppY[binIndex] * F2[binIndex] * P1[binIndex] + - ppY[binIndex] * F1[binIndex] * F2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + - P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) * - (-1 + 2 * F1[binIndex]) / - ((factor2 * factor2) * (-1 + P2[binIndex])); - const double factor3 = (-1 + P2[binIndex]); - const double dP2 = - -(ppY[binIndex] * F1[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] + - 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] + - ppY[binIndex] * P1[binIndex] - P1[binIndex] * mpY[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + - P1[binIndex] * mmY[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (-1 + P2[binIndex])) + - (ppY[binIndex] * P2[binIndex] * F1[binIndex] - - 2 * ppY[binIndex] * F2[binIndex] * P1[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P2[binIndex] - - 2 * ppY[binIndex] * P2[binIndex] * F1[binIndex] * P1[binIndex] + - 2 * P1[binIndex] * mpY[binIndex] * F2[binIndex] * P2[binIndex] + - ppY[binIndex] * P1[binIndex] * P2[binIndex] - - P1[binIndex] * mpY[binIndex] * P2[binIndex] + - 4 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] * - P2[binIndex] + - P1[binIndex] * mmY[binIndex] * P2[binIndex] - - ppY[binIndex] * F1[binIndex] + - 2 * ppY[binIndex] * F1[binIndex] * P1[binIndex] - - P1[binIndex] * mmY[binIndex] - - P1[binIndex] * mpY[binIndex] * F2[binIndex] + - ppY[binIndex] * F2[binIndex] * P1[binIndex] + - ppY[binIndex] * F1[binIndex] * F2[binIndex] - - 2 * ppY[binIndex] * F1[binIndex] * F2[binIndex] * P1[binIndex] + - P1[binIndex] * mpY[binIndex] - ppY[binIndex] * P1[binIndex]) / - ((-P1[binIndex] + 2 * F1[binIndex] * P1[binIndex] - - F1[binIndex]) * - (factor3 * factor3)); - const double e1 = dI00 * ppE[binIndex]; - const double e2 = dI11 * mmE[binIndex]; - const double e3 = dI10 * mpE[binIndex]; - const double e4 = dF1 * F1E[binIndex]; - const double e5 = dF2 * F2E[binIndex]; - const double e6 = dP1 * P1E[binIndex]; - const double e7 = dP2 * P2E[binIndex]; - pmE[binIndex] = std::sqrt(e1 * e1 + e2 * e2 + e3 * e3 + e4 * e4 + - e5 * e5 + e6 * e6 + e7 * e7); - } - } +private: + std::vector<MatrixWorkspace_sptr> createWorkspaces(int n) { + std::vector<MatrixWorkspace_sptr> workspaces; + for (int i = 0; i < n; ++i) { + auto ws = create1DWorkspaceConstant(5, 2.0, 1.0, true); + workspaces.push_back(ws); } + return workspaces; } -}; -class PolarizationEfficiencyCorTestPerformance : public CxxTest::TestSuite { -public: - void setUp() override { - using namespace Mantid::API; - auto loadWS = - AlgorithmManager::Instance().createUnmanaged("LoadILLReflectometry"); - loadWS->setChild(true); - loadWS->initialize(); - loadWS->setProperty("Filename", "ILL/D17/317370.nxs"); - loadWS->setProperty("OutputWorkspace", "output"); - loadWS->setProperty("XUnit", "TimeOfFlight"); - loadWS->execute(); - m_ws00 = loadWS->getProperty("OutputWorkspace"); - auto groupDetectors = - AlgorithmManager::Instance().createUnmanaged("GroupDetectors"); - groupDetectors->setChild(true); - groupDetectors->initialize(); - groupDetectors->setProperty("InputWorkspace", m_ws00); - groupDetectors->setProperty("OutputWorkspace", "output"); - groupDetectors->setPropertyValue("WorkspaceIndexList", "201, 202, 203"); - groupDetectors->execute(); - m_ws00 = groupDetectors->getProperty("OutputWorkspace"); - auto convertUnits = - AlgorithmManager::Instance().createUnmanaged("ConvertUnits"); - convertUnits->setChild(true); - convertUnits->initialize(); - convertUnits->setProperty("InputWorkspace", m_ws00); - convertUnits->setProperty("OutputWorkspace", "output"); - convertUnits->setProperty("Target", "Wavelength"); - convertUnits->execute(); - m_ws00 = convertUnits->getProperty("OutputWorkspace"); - auto crop = AlgorithmManager::Instance().createUnmanaged("CropWorkspace"); - crop->setChild(true); - crop->initialize(); - crop->setProperty("InputWorkspace", m_ws00); - crop->setProperty("OutputWorkspace", "output"); - crop->setProperty("XMin", 0.); - crop->execute(); - m_ws00 = crop->getProperty("OutputWorkspace"); - AnalysisDataService::Instance().addOrReplace("00", m_ws00); - m_ws01 = m_ws00->clone(); - AnalysisDataService::Instance().addOrReplace("01", m_ws01); - m_ws10 = m_ws00->clone(); - AnalysisDataService::Instance().addOrReplace("10", m_ws10); - m_ws11 = m_ws00->clone(); - AnalysisDataService::Instance().addOrReplace("11", m_ws11); - auto loadEff = AlgorithmManager::Instance().createUnmanaged( - "LoadILLPolarizationFactors"); - loadEff->setChild(true); - loadEff->initialize(); - loadEff->setProperty("Filename", "ILL/D17/PolarizationFactors.txt"); - loadEff->setProperty("OutputWorkspace", "output"); - loadEff->setProperty("WavelengthReference", m_ws00); - loadEff->execute(); - m_effWS = loadEff->getProperty("OutputWorkspace"); - } - - void tearDown() override { - using namespace Mantid::API; - AnalysisDataService::Instance().clear(); - } - - void test_DirectBeamPerformance() { - using namespace Mantid::API; - for (int i = 0; i < 3000; ++i) { - PolarizationEfficiencyCor correction; - correction.setChild(true); - correction.setRethrows(true); - correction.initialize(); - correction.setProperty("InputWorkspaces", "00"); - correction.setProperty("OutputWorkspace", "output"); - correction.setProperty("Flippers", "0"); - correction.setProperty("Efficiencies", m_effWS); - TS_ASSERT_THROWS_NOTHING(correction.execute()) + WorkspaceGroup_sptr createWorkspaceGroup(int n) { + auto group = boost::make_shared<WorkspaceGroup>(); + auto workspaces = createWorkspaces(n); + for (auto &ws : workspaces) { + ws->getAxis(0)->setUnit("Wavelength"); + group->addWorkspace(ws); } - } - - void test_ThreeInputsPerformanceMissing01() { - using namespace Mantid::API; - for (int i = 0; i < 3000; ++i) { - PolarizationEfficiencyCor correction; - correction.setChild(true); - correction.setRethrows(true); - correction.initialize(); - correction.setProperty("InputWorkspaces", "00, 10, 11"); - correction.setProperty("OutputWorkspace", "output"); - correction.setProperty("Flippers", "00, 10, 11"); - correction.setProperty("Efficiencies", m_effWS); - TS_ASSERT_THROWS_NOTHING(correction.execute()) + AnalysisDataService::Instance().addOrReplace("WS_GROUP_1", group); + return group; + } + + std::vector<std::string> createWorkspacesInADS(int n) { + std::vector<std::string> names; + auto workspaces = createWorkspaces(n); + size_t i = 0; + for (auto &ws : workspaces) { + names.push_back("ws_" + std::to_string(i)); + AnalysisDataService::Instance().addOrReplace(names.back(), ws); + ++i; } - } - - void test_ThreeInputsPerformanceMissing10() { - using namespace Mantid::API; - for (int i = 0; i < 3000; ++i) { - PolarizationEfficiencyCor correction; - correction.setChild(true); - correction.setRethrows(true); - correction.initialize(); - correction.setProperty("InputWorkspaces", "00, 01, 11"); - correction.setProperty("OutputWorkspace", "output"); - correction.setProperty("Flippers", "00, 01, 11"); - correction.setProperty("Efficiencies", m_effWS); - TS_ASSERT_THROWS_NOTHING(correction.execute()) + return names; + } + + MatrixWorkspace_sptr createEfficiencies(std::string const &kind) { + static std::map<std::string, std::vector<std::string>> const labels = { + {"Wildes", {"P1", "P2", "F1", "F2"}}, + {"Fredrikze", {"Pp", "Ap", "Rho", "Alpha"}}}; + if (kind == "Wildes" || kind == "Fredrikze") { + auto inWS = createWorkspaces(1)[0]; + MatrixWorkspace_sptr ws = WorkspaceFactory::Instance().create(inWS, 4); + ws->getAxis(0)->setUnit("Wavelength"); + auto axis1 = new TextAxis(4); + ws->replaceAxis(1, axis1); + auto const ¤t_labels = labels.at(kind); + for (size_t i = 0; i < ws->getNumberHistograms(); ++i) { + axis1->setLabel(i, current_labels[i]); + } + return ws; + } else if (kind == "histo") { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(10, 0, 10); + auto ws3 = createHistoWS(10, 0, 10); + auto ws4 = createHistoWS(10, 0, 10); + + auto alg = AlgorithmFactory::Instance().create( + "JoinISISPolarizationEfficiencies", -1); + alg->initialize(); + alg->setChild(true); + alg->setRethrows(true); + alg->setProperty("P1", ws1); + alg->setProperty("P2", ws2); + alg->setProperty("F1", ws3); + alg->setProperty("F2", ws4); + alg->setPropertyValue("OutputWorkspace", "dummy"); + alg->execute(); + MatrixWorkspace_sptr outWS = alg->getProperty("OutputWorkspace"); + return outWS; + } else if (kind == "points") { + auto ws1 = createPointWS(10, 0, 10); + auto ws2 = createPointWS(10, 0, 10); + auto ws3 = createPointWS(10, 0, 10); + auto ws4 = createPointWS(10, 0, 10); + + auto alg = AlgorithmFactory::Instance().create( + "JoinISISPolarizationEfficiencies", -1); + alg->initialize(); + alg->setChild(true); + alg->setRethrows(true); + alg->setProperty("P1", ws1); + alg->setProperty("P2", ws2); + alg->setProperty("F1", ws3); + alg->setProperty("F2", ws4); + alg->setPropertyValue("OutputWorkspace", "dummy"); + alg->execute(); + MatrixWorkspace_sptr outWS = alg->getProperty("OutputWorkspace"); + return outWS; + } else if (kind == "points-short") { + auto ws1 = createPointWS(4, 0, 10); + auto ws2 = createPointWS(4, 0, 10); + auto ws3 = createPointWS(4, 0, 10); + auto ws4 = createPointWS(4, 0, 10); + + auto alg = AlgorithmFactory::Instance().create( + "JoinISISPolarizationEfficiencies", -1); + alg->initialize(); + alg->setChild(true); + alg->setRethrows(true); + alg->setProperty("P1", ws1); + alg->setProperty("P2", ws2); + alg->setProperty("F1", ws3); + alg->setProperty("F2", ws4); + alg->setPropertyValue("OutputWorkspace", "dummy"); + alg->execute(); + MatrixWorkspace_sptr outWS = alg->getProperty("OutputWorkspace"); + return outWS; } + throw std::logic_error("Unknown efficeincy test kind"); } - void test_TwoInputsNoAnalyzerPerformance() { - using namespace Mantid::API; - for (int i = 0; i < 3000; ++i) { - PolarizationEfficiencyCor correction; - correction.setChild(true); - correction.setRethrows(true); - correction.initialize(); - correction.setProperty("InputWorkspaces", "00, 11"); - correction.setProperty("OutputWorkspace", "output"); - correction.setProperty("Flippers", "0, 1"); - correction.setProperty("Efficiencies", m_effWS); - TS_ASSERT_THROWS_NOTHING(correction.execute()) - } + MatrixWorkspace_sptr createHistoWS(size_t size, double startX, + double endX) const { + double const dX = (endX - startX) / double(size); + BinEdges xVals(size + 1, LinearGenerator(startX, dX)); + Counts yVals(size, 1.0); + auto retVal = boost::make_shared<Workspace2D>(); + retVal->initialize(1, Histogram(xVals, yVals)); + return retVal; } - void test_TwoInputsPerformance() { - using namespace Mantid::API; - for (int i = 0; i < 3000; ++i) { - PolarizationEfficiencyCor correction; - correction.setChild(true); - correction.setRethrows(true); - correction.initialize(); - correction.setProperty("InputWorkspaces", "00, 11"); - correction.setProperty("OutputWorkspace", "output"); - correction.setProperty("Flippers", "00, 11"); - correction.setProperty("Efficiencies", m_effWS); - TS_ASSERT_THROWS_NOTHING(correction.execute()) - } + MatrixWorkspace_sptr createPointWS(size_t size, double startX, + double endX) const { + double const dX = (endX - startX) / double(size - 1); + Points xVals(size, LinearGenerator(startX, dX)); + Counts yVals(size, 1.0); + auto retVal = boost::make_shared<Workspace2D>(); + retVal->initialize(1, Histogram(xVals, yVals)); + return retVal; } - -private: - Mantid::API::MatrixWorkspace_sptr m_effWS; - Mantid::API::MatrixWorkspace_sptr m_ws00; - Mantid::API::MatrixWorkspace_sptr m_ws01; - Mantid::API::MatrixWorkspace_sptr m_ws10; - Mantid::API::MatrixWorkspace_sptr m_ws11; }; #endif /* MANTID_ALGORITHMS_POLARIZATIONEFFICIENCYCORTEST_H_ */ diff --git a/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h b/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h index 328fd8d98ea89de92988c67e1981416f07747db4..8179b8eac358dc690d3d564f5817b339555586a3 100644 --- a/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h +++ b/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h @@ -564,6 +564,88 @@ public: TS_ASSERT_DELTA(outQ->x(0)[0], 0.3353, 0.0001); TS_ASSERT_DELTA(outQ->x(0)[7], 0.5962, 0.0001); } + + void test_polarization_correction() { + + MatrixWorkspace_sptr first = m_TOF->clone(); + MatrixWorkspace_sptr second = m_TOF->clone(); + MatrixWorkspace_sptr third = m_TOF->clone(); + MatrixWorkspace_sptr fourth = m_TOF->clone(); + + WorkspaceGroup_sptr inputWSGroup = boost::make_shared<WorkspaceGroup>(); + inputWSGroup->addWorkspace(first); + inputWSGroup->addWorkspace(second); + inputWSGroup->addWorkspace(third); + inputWSGroup->addWorkspace(fourth); + WorkspaceGroup_sptr transWSGroup = boost::make_shared<WorkspaceGroup>(); + transWSGroup->addWorkspace(first); + transWSGroup->addWorkspace(second); + transWSGroup->addWorkspace(third); + transWSGroup->addWorkspace(fourth); + AnalysisDataService::Instance().addOrReplace("input", inputWSGroup); + AnalysisDataService::Instance().addOrReplace("trans", transWSGroup); + + ReflectometryReductionOneAuto2 alg; + alg.initialize(); + alg.setPropertyValue("InputWorkspace", "input"); + alg.setPropertyValue("FirstTransmissionRun", "trans"); + alg.setProperty("WavelengthMin", 1.5); + alg.setProperty("WavelengthMax", 15.0); + alg.setProperty("ProcessingInstructions", "2"); + alg.setProperty("MomentumTransferStep", 0.04); + alg.setProperty("PolarizationAnalysis", "PA"); + alg.setProperty("Pp", "1,1,2"); + alg.setProperty("Ap", "1,1,2"); + alg.setProperty("Rho", "1,1"); + alg.setProperty("Alpha", "1"); + alg.setPropertyValue("OutputWorkspace", "IvsQ"); + alg.setPropertyValue("OutputWorkspaceBinned", "IvsQ_binned"); + alg.setPropertyValue("OutputWorkspaceWavelength", "IvsLam"); + alg.execute(); + auto outQGroup = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("IvsQ"); + auto outLamGroup = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("IvsLam"); + + TS_ASSERT(outQGroup); + TS_ASSERT(outLamGroup); + + if (!outQGroup || !outLamGroup) + return; + + TS_ASSERT_EQUALS(outQGroup->size(), 4); + TS_ASSERT_EQUALS(outLamGroup->size(), 4); + + { + auto outQ = + boost::dynamic_pointer_cast<MatrixWorkspace>(outQGroup->getItem(0)); + TS_ASSERT_EQUALS(outQ->getNumberHistograms(), 1); + TS_ASSERT_EQUALS(outQ->blocksize(), 14); + // X range in outQ + TS_ASSERT_DELTA(outQ->x(0)[0], 0.3353, 0.0001); + TS_ASSERT_DELTA(outQ->x(0)[7], 0.5962, 0.0001); + auto outLam = + boost::dynamic_pointer_cast<MatrixWorkspace>(outLamGroup->getItem(0)); + // X range in outLam + TS_ASSERT_DELTA(outLam->x(0)[0], 1.7924, 0.0001); + TS_ASSERT_DELTA(outLam->x(0)[7], 8.0658, 0.0001); + } + + { + auto outQ = + boost::dynamic_pointer_cast<MatrixWorkspace>(outQGroup->getItem(1)); + TS_ASSERT_EQUALS(outQ->getNumberHistograms(), 1); + TS_ASSERT_EQUALS(outQ->blocksize(), 14); + // X range in outQ + TS_ASSERT_DELTA(outQ->x(0)[0], 0.3353, 0.0001); + TS_ASSERT_DELTA(outQ->x(0)[7], 0.5962, 0.0001); + auto outLam = + boost::dynamic_pointer_cast<MatrixWorkspace>(outLamGroup->getItem(1)); + // X range in outLam + TS_ASSERT_DELTA(outLam->x(0)[0], 1.7924, 0.0001); + TS_ASSERT_DELTA(outLam->x(0)[7], 8.0658, 0.0001); + } + } }; #endif /* MANTID_ALGORITHMS_REFLECTOMETRYREDUCTIONONEAUTO2TEST_H_ */ diff --git a/Framework/DataHandling/CMakeLists.txt b/Framework/DataHandling/CMakeLists.txt index ac72a225b3cb983663e85098436aabeaebe43f9f..220c1e62f1facd25ab13c450db3d99f91e1a1663 100644 --- a/Framework/DataHandling/CMakeLists.txt +++ b/Framework/DataHandling/CMakeLists.txt @@ -7,6 +7,8 @@ set ( SRC_FILES src/CreateChopperModel.cpp src/CreateChunkingFromInstrument.cpp src/CreateModeratorModel.cpp + src/CreatePolarizationEfficiencies.cpp + src/CreatePolarizationEfficienciesBase.cpp src/CreateSampleShape.cpp src/CreateSimulationWorkspace.cpp src/DataBlock.cpp @@ -29,6 +31,7 @@ set ( SRC_FILES src/H5Util.cpp src/ISISDataArchive.cpp src/ISISRunLogs.cpp + src/JoinISISPolarizationEfficiencies.cpp src/Load.cpp src/LoadANSTOHelper.cpp src/LoadAscii.cpp @@ -60,6 +63,7 @@ set ( SRC_FILES src/LoadILLSANS.cpp src/LoadILLTOF2.cpp src/LoadISISNexus2.cpp + src/LoadISISPolarizationEfficiencies.cpp src/LoadInstrument.cpp src/LoadInstrumentFromNexus.cpp src/LoadInstrumentFromRaw.cpp @@ -190,6 +194,8 @@ set ( INC_FILES inc/MantidDataHandling/CreateChopperModel.h inc/MantidDataHandling/CreateChunkingFromInstrument.h inc/MantidDataHandling/CreateModeratorModel.h + inc/MantidDataHandling/CreatePolarizationEfficiencies.h + inc/MantidDataHandling/CreatePolarizationEfficienciesBase.h inc/MantidDataHandling/CreateSampleShape.h inc/MantidDataHandling/CreateSimulationWorkspace.h inc/MantidDataHandling/DataBlock.h @@ -212,6 +218,7 @@ set ( INC_FILES inc/MantidDataHandling/H5Util.h inc/MantidDataHandling/ISISDataArchive.h inc/MantidDataHandling/ISISRunLogs.h + inc/MantidDataHandling/JoinISISPolarizationEfficiencies.h inc/MantidDataHandling/Load.h inc/MantidDataHandling/LoadANSTOHelper.h inc/MantidDataHandling/LoadAscii.h @@ -243,6 +250,7 @@ set ( INC_FILES inc/MantidDataHandling/LoadILLSANS.h inc/MantidDataHandling/LoadILLTOF2.h inc/MantidDataHandling/LoadISISNexus2.h + inc/MantidDataHandling/LoadISISPolarizationEfficiencies.h inc/MantidDataHandling/LoadInstrument.h inc/MantidDataHandling/LoadInstrumentFromNexus.h inc/MantidDataHandling/LoadInstrumentFromRaw.h @@ -372,6 +380,7 @@ set ( TEST_FILES CreateChopperModelTest.h CreateChunkingFromInstrumentTest.h CreateModeratorModelTest.h + CreatePolarizationEfficienciesTest.h CreateSampleShapeTest.h CreateSimulationWorkspaceTest.h DataBlockCompositeTest.h @@ -393,6 +402,7 @@ set ( TEST_FILES H5UtilTest.h ISISDataArchiveTest.h InstrumentRayTracerTest.h + JoinISISPolarizationEfficienciesTest.h LoadAscii2Test.h LoadAsciiTest.h LoadBBYTest.h @@ -419,6 +429,7 @@ set ( TEST_FILES LoadILLSANSTest.h LoadILLTOF2Test.h LoadISISNexusTest.h + LoadISISPolarizationEfficienciesTest.h LoadInstrumentFromNexusTest.h LoadInstrumentFromRawTest.h LoadInstrumentTest.h diff --git a/Framework/DataHandling/inc/MantidDataHandling/CreatePolarizationEfficiencies.h b/Framework/DataHandling/inc/MantidDataHandling/CreatePolarizationEfficiencies.h new file mode 100644 index 0000000000000000000000000000000000000000..866107fb77eeab024bb62d0cb44d198f1adae191 --- /dev/null +++ b/Framework/DataHandling/inc/MantidDataHandling/CreatePolarizationEfficiencies.h @@ -0,0 +1,50 @@ +#ifndef MANTID_DATAHANDLING_CREATEPOLARIZATIONEFFICIENCIES_H_ +#define MANTID_DATAHANDLING_CREATEPOLARIZATIONEFFICIENCIES_H_ + +#include "MantidKernel/System.h" +#include "MantidDataHandling/CreatePolarizationEfficienciesBase.h" + +namespace Mantid { +namespace DataHandling { + +/** CreatePolarizationEfficiencies + + Copyright © 2014 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> + */ +class DLLExport CreatePolarizationEfficiencies + : public CreatePolarizationEfficienciesBase { +public: + const std::string name() const override; + int version() const override; + const std::string summary() const override; + const std::vector<std::string> seeAlso() const override; + +private: + void init() override; + API::MatrixWorkspace_sptr + createEfficiencies(std::vector<std::string> const &labels) override; +}; + +} // namespace DataHandling +} // namespace Mantid + +#endif /* MANTID_DATAHANDLING_CREATEPOLARIZATIONEFFICIENCIES_H_ */ diff --git a/Framework/DataHandling/inc/MantidDataHandling/CreatePolarizationEfficienciesBase.h b/Framework/DataHandling/inc/MantidDataHandling/CreatePolarizationEfficienciesBase.h new file mode 100644 index 0000000000000000000000000000000000000000..946505d59fa86c0e767c975f294f46d45e70e8b0 --- /dev/null +++ b/Framework/DataHandling/inc/MantidDataHandling/CreatePolarizationEfficienciesBase.h @@ -0,0 +1,71 @@ +#ifndef MANTID_DATAHANDLING_CREATEPOLARIZATIONEFFICIENCIESBASE_H_ +#define MANTID_DATAHANDLING_CREATEPOLARIZATIONEFFICIENCIESBASE_H_ + +#include "MantidKernel/System.h" +#include "MantidAPI/Algorithm.h" +#include "MantidAPI/MatrixWorkspace_fwd.h" +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> + +namespace Mantid { +namespace DataHandling { + +/** CreatePolarizationEfficienciesBase - the base class for algorithms + that create polarization efficiency workspaces: + + - CreatePolarizationEfficiencies + - JoinISISPolarizationEfficiencies + - LoadISISPolarizationEfficiencies + + Copyright © 2014 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> + */ +class DLLExport CreatePolarizationEfficienciesBase : public API::Algorithm { +public: + const std::string category() const override; + +protected: + void initOutputWorkspace(); + std::vector<std::string> + getNonDefaultProperties(std::vector<std::string> const &props) const; + + /// Names of the efficiency properties + static std::string const Pp; + static std::string const Ap; + static std::string const Rho; + static std::string const Alpha; + static std::string const P1; + static std::string const P2; + static std::string const F1; + static std::string const F2; + +private: + void exec() override; + /// Create the output workspace with efficiencies + /// @param labels :: Names of the efficiencies to create + virtual API::MatrixWorkspace_sptr + createEfficiencies(std::vector<std::string> const &labels) = 0; +}; + +} // namespace DataHandling +} // namespace Mantid + +#endif /* MANTID_DATAHANDLING_CREATEPOLARIZATIONEFFICIENCIESBASE_H_ */ diff --git a/Framework/DataHandling/inc/MantidDataHandling/JoinISISPolarizationEfficiencies.h b/Framework/DataHandling/inc/MantidDataHandling/JoinISISPolarizationEfficiencies.h new file mode 100644 index 0000000000000000000000000000000000000000..1c154187cee55be1e2d2132654d167364cdac419 --- /dev/null +++ b/Framework/DataHandling/inc/MantidDataHandling/JoinISISPolarizationEfficiencies.h @@ -0,0 +1,62 @@ +#ifndef MANTID_DATAHANDLING_JOINISISPOLARIZATIONEFFICIENCIES_H_ +#define MANTID_DATAHANDLING_JOINISISPOLARIZATIONEFFICIENCIES_H_ + +#include "MantidDataHandling/CreatePolarizationEfficienciesBase.h" +#include "MantidDataHandling/DllConfig.h" + +namespace Mantid { +namespace DataHandling { + +/** JoinISISPolarizationEfficiencies : Joins reflectometry polarization + efficiency correction factors to form a single matrix workspace. + + Copyright © 2017 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> +*/ +class MANTID_DATAHANDLING_DLL JoinISISPolarizationEfficiencies + : public CreatePolarizationEfficienciesBase { +public: + const std::string name() const override; + int version() const override; + const std::string summary() const override; + const std::vector<std::string> seeAlso() const override; + +private: + void init() override; + API::MatrixWorkspace_sptr + createEfficiencies(std::vector<std::string> const &props) override; + API::MatrixWorkspace_sptr + createEfficiencies(std::vector<std::string> const &labels, + std::vector<API::MatrixWorkspace_sptr> const &workspaces); + std::vector<API::MatrixWorkspace_sptr> interpolateWorkspaces( + std::vector<API::MatrixWorkspace_sptr> const &workspaces); + API::MatrixWorkspace_sptr + interpolatePointDataWorkspace(API::MatrixWorkspace_sptr ws, + size_t const maxSize); + API::MatrixWorkspace_sptr + interpolateHistogramWorkspace(API::MatrixWorkspace_sptr ws, + size_t const maxSize); +}; + +} // namespace DataHandling +} // namespace Mantid + +#endif /* MANTID_DATAHANDLING_JOINISISPOLARIZATIONEFFICIENCIES_H_ */ diff --git a/Framework/DataHandling/inc/MantidDataHandling/LoadISISPolarizationEfficiencies.h b/Framework/DataHandling/inc/MantidDataHandling/LoadISISPolarizationEfficiencies.h new file mode 100644 index 0000000000000000000000000000000000000000..e341b5bacbabe575f650a5ad55db9922b84aced6 --- /dev/null +++ b/Framework/DataHandling/inc/MantidDataHandling/LoadISISPolarizationEfficiencies.h @@ -0,0 +1,51 @@ +#ifndef MANTID_DATAHANDLING_LOADISISPOLARIZATIONEFFICIENCIES_H_ +#define MANTID_DATAHANDLING_LOADISISPOLARIZATIONEFFICIENCIES_H_ + +#include "MantidDataHandling/CreatePolarizationEfficienciesBase.h" +#include "MantidDataHandling/DllConfig.h" + +namespace Mantid { +namespace DataHandling { + +/** LoadISISPolarizationEfficiencies : Load reflectometry polarization + efficiency correction factors from disk. + + Copyright © 2017 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> +*/ +class MANTID_DATAHANDLING_DLL LoadISISPolarizationEfficiencies + : public CreatePolarizationEfficienciesBase { +public: + const std::string name() const override; + int version() const override; + const std::string summary() const override; + const std::vector<std::string> seeAlso() const override; + +private: + void init() override; + API::MatrixWorkspace_sptr + createEfficiencies(std::vector<std::string> const &props) override; +}; + +} // namespace DataHandling +} // namespace Mantid + +#endif /* MANTID_DATAHANDLING_LOADISISPOLARIZATIONEFFICIENCIES_H_ */ diff --git a/Framework/DataHandling/src/CreatePolarizationEfficiencies.cpp b/Framework/DataHandling/src/CreatePolarizationEfficiencies.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a6cce278dc695fc9a896e75773376b0c60fedbfe --- /dev/null +++ b/Framework/DataHandling/src/CreatePolarizationEfficiencies.cpp @@ -0,0 +1,142 @@ +#include "MantidDataHandling/CreatePolarizationEfficiencies.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/TextAxis.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidAPI/WorkspaceHistory.h" +#include "MantidDataObjects/WorkspaceSingleValue.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/ListValidator.h" +#include "MantidKernel/Unit.h" +#include "MantidGeometry/Instrument.h" + +#include <boost/shared_ptr.hpp> + +#include <algorithm> + +using namespace Mantid::API; +using namespace Mantid::Kernel; +using namespace Mantid::Geometry; + +namespace { + +double calculatePolynomial(std::vector<double> const &coefficients, double x) { + double polynomial = coefficients[0]; + double xPow = 1.0; + // Build up the polynomial in ascending powers of x + for (size_t i = 1; i < coefficients.size(); ++i) { + xPow *= x; + polynomial += coefficients[i] * xPow; + } + return polynomial; +} + +} // namespace + +namespace Mantid { +namespace DataHandling { + +DECLARE_ALGORITHM(CreatePolarizationEfficiencies) + +const std::string CreatePolarizationEfficiencies::name() const { + return "CreatePolarizationEfficiencies"; +} + +int CreatePolarizationEfficiencies::version() const { return 1; } + +const std::string CreatePolarizationEfficiencies::summary() const { + return "Converts polynomial factors to histograms with polarization " + "efficiencies."; +} + +const std::vector<std::string> CreatePolarizationEfficiencies::seeAlso() const { + return {"JoinISISPolarizationEfficiencies", + "LoadISISPolarizationEfficiencies", "PolarizationEfficiencyCor"}; +} + +void CreatePolarizationEfficiencies::init() { + declareProperty(make_unique<WorkspaceProperty<Mantid::API::MatrixWorkspace>>( + "InputWorkspace", "", Direction::Input), + "An input workspace to use the x-values from."); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(Pp, Direction::Input), + "Effective polarizing power of the polarizing system. " + "Expressed as a ratio 0 < Pp < 1"); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(Ap, Direction::Input), + "Effective polarizing power of the analyzing system. " + "Expressed as a ratio 0 < Ap < 1"); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(Rho, Direction::Input), + "Ratio of efficiencies of polarizer spin-down to polarizer " + "spin-up. This is characteristic of the polarizer flipper. " + "Values are constants for each term in a polynomial " + "expression."); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(Alpha, Direction::Input), + "Ratio of efficiencies of analyzer spin-down to analyzer " + "spin-up. This is characteristic of the analyzer flipper. " + "Values are factors for each term in a polynomial " + "expression."); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(P1, Direction::Input), + "Polarizer efficiency."); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(P2, Direction::Input), + "Analyzer efficiency."); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(F1, Direction::Input), + "Polarizer flipper efficiency."); + + declareProperty( + Kernel::make_unique<ArrayProperty<double>>(F2, Direction::Input), + "Analyzer flipper efficiency."); + + initOutputWorkspace(); +} + +/// Create the efficiencies workspace given names of input properties. +/// @param labels :: Names of efficiencies which to include in the output +/// workspace. +MatrixWorkspace_sptr CreatePolarizationEfficiencies::createEfficiencies( + std::vector<std::string> const &labels) { + + std::vector<std::vector<double>> polynomialCoefficients; + + for (auto const &label : labels) { + polynomialCoefficients.emplace_back<std::vector<double>>( + getProperty(label)); + } + + MatrixWorkspace_sptr inWS = getProperty("InputWorkspace"); + auto sharedInX = inWS->sharedX(0); + + MatrixWorkspace_sptr outWS = WorkspaceFactory::Instance().create( + inWS, labels.size(), sharedInX->size(), inWS->blocksize()); + auto axis1 = new TextAxis(labels.size()); + outWS->replaceAxis(1, axis1); + outWS->getAxis(0)->setUnit(inWS->getAxis(0)->unit()->unitID()); + + auto const x = inWS->points(0); + std::vector<double> y(x.size()); + for (size_t i = 0; i < labels.size(); ++i) { + outWS->setSharedX(i, sharedInX); + auto const &coefficients = polynomialCoefficients[i]; + std::transform(x.begin(), x.end(), y.begin(), [&coefficients](double v) { + return calculatePolynomial(coefficients, v); + }); + outWS->mutableY(i) = y; + axis1->setLabel(i, labels[i]); + } + + return outWS; +} + +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/DataHandling/src/CreatePolarizationEfficienciesBase.cpp b/Framework/DataHandling/src/CreatePolarizationEfficienciesBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a3747f42a619cabf1533d87ecda9813b636c812 --- /dev/null +++ b/Framework/DataHandling/src/CreatePolarizationEfficienciesBase.cpp @@ -0,0 +1,81 @@ +#include "MantidDataHandling/CreatePolarizationEfficienciesBase.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/TextAxis.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidAPI/WorkspaceHistory.h" +#include "MantidDataObjects/WorkspaceSingleValue.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/ListValidator.h" +#include "MantidKernel/Unit.h" +#include "MantidGeometry/Instrument.h" + +#include <boost/shared_ptr.hpp> + +#include <algorithm> + +using namespace Mantid::API; +using namespace Mantid::Kernel; +using namespace Mantid::Geometry; + +namespace Mantid { +namespace DataHandling { + +std::string const CreatePolarizationEfficienciesBase::Pp("Pp"); +std::string const CreatePolarizationEfficienciesBase::Ap("Ap"); +std::string const CreatePolarizationEfficienciesBase::Rho("Rho"); +std::string const CreatePolarizationEfficienciesBase::Alpha("Alpha"); +std::string const CreatePolarizationEfficienciesBase::P1("P1"); +std::string const CreatePolarizationEfficienciesBase::P2("P2"); +std::string const CreatePolarizationEfficienciesBase::F1("F1"); +std::string const CreatePolarizationEfficienciesBase::F2("F2"); + +const std::string CreatePolarizationEfficienciesBase::category() const { + return "Reflectometry"; +} + +void CreatePolarizationEfficienciesBase::initOutputWorkspace() { + declareProperty(make_unique<WorkspaceProperty<Mantid::API::MatrixWorkspace>>( + "OutputWorkspace", "", Direction::Output), + "An output workspace."); +} + +void CreatePolarizationEfficienciesBase::exec() { + auto const labelsFredrikze = getNonDefaultProperties({Pp, Ap, Rho, Alpha}); + auto const labelsWildes = getNonDefaultProperties({P1, P2, F1, F2}); + + if (labelsFredrikze.empty() && labelsWildes.empty()) { + throw std::invalid_argument( + "At least one of the efficiencies must be set."); + } + + if (!labelsFredrikze.empty() && !labelsWildes.empty()) { + throw std::invalid_argument( + "Efficiencies belonging to different methods cannot mix."); + } + + MatrixWorkspace_sptr efficiencies; + if (!labelsFredrikze.empty()) { + efficiencies = createEfficiencies(labelsFredrikze); + } else { + efficiencies = createEfficiencies(labelsWildes); + } + + setProperty("OutputWorkspace", efficiencies); +} + +/// Get names of non-default properties out of a list of names +/// @param labels :: Names of properties to check. +std::vector<std::string> +CreatePolarizationEfficienciesBase::getNonDefaultProperties( + std::vector<std::string> const &labels) const { + std::vector<std::string> outputLabels; + for (auto const &label : labels) { + if (!isDefault(label)) { + outputLabels.emplace_back(label); + } + } + return outputLabels; +} + +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/DataHandling/src/JoinISISPolarizationEfficiencies.cpp b/Framework/DataHandling/src/JoinISISPolarizationEfficiencies.cpp new file mode 100644 index 0000000000000000000000000000000000000000..96b5825b8ae470e1c0ca5b10db48045bf14e324b --- /dev/null +++ b/Framework/DataHandling/src/JoinISISPolarizationEfficiencies.cpp @@ -0,0 +1,234 @@ +#include "MantidDataHandling/JoinISISPolarizationEfficiencies.h" + +#include "MantidAPI/FileProperty.h" +#include "MantidAPI/TextAxis.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidHistogramData/Histogram.h" +#include "MantidHistogramData/Interpolate.h" +#include "MantidHistogramData/LinearGenerator.h" +#include "MantidKernel/make_unique.h" +#include <limits> + +using namespace Mantid::API; +using namespace Mantid::DataObjects; +using namespace Mantid::HistogramData; +using namespace Mantid::Kernel; + +namespace Mantid { +namespace DataHandling { + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(JoinISISPolarizationEfficiencies) + +//---------------------------------------------------------------------------------------------- + +/// Algorithms name for identification. @see Algorithm::name +const std::string JoinISISPolarizationEfficiencies::name() const { + return "JoinISISPolarizationEfficiencies"; +} + +/// Algorithm's version for identification. @see Algorithm::version +int JoinISISPolarizationEfficiencies::version() const { return 1; } + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string JoinISISPolarizationEfficiencies::summary() const { + return "Joins workspaces containing ISIS reflectometry polarization " + "efficiency factors into a single workspace ready to be used with " + "PolarizationEfficiencyCor."; +} + +const std::vector<std::string> +JoinISISPolarizationEfficiencies::seeAlso() const { + return {"CreatePolarizationEfficiencies", "LoadISISPolarizationEfficiencies", + "PolarizationEfficiencyCor"}; +} + +//---------------------------------------------------------------------------------------------- +/** Initialize the algorithm's properties. + */ +void JoinISISPolarizationEfficiencies::init() { + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + Pp, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the Pp polarization efficiency."); + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + Ap, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the Ap polarization efficiency."); + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + Rho, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the Rho polarization efficiency."); + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + Alpha, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the Alpha polarization efficiency."); + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + P1, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the P1 polarization efficiency."); + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + P2, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the P2 polarization efficiency."); + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + F1, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the F1 polarization efficiency."); + + declareProperty( + Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>( + F2, "", Kernel::Direction::Input, PropertyMode::Optional), + "A matrix workspaces containing the F2 polarization efficiency."); + + initOutputWorkspace(); +} + +/// Load efficientcies from files and put them into a single workspace. +/// @param props :: Names of properties containg names of files to load. +MatrixWorkspace_sptr JoinISISPolarizationEfficiencies::createEfficiencies( + std::vector<std::string> const &props) { + std::vector<MatrixWorkspace_sptr> workspaces; + for (auto const &propName : props) { + MatrixWorkspace_sptr ws = getProperty(propName); + if (ws->getNumberHistograms() != 1) { + throw std::runtime_error( + "Loaded workspace must contain a single histogram. Found " + + std::to_string(ws->getNumberHistograms())); + } + workspaces.push_back(ws); + } + + return createEfficiencies(props, workspaces); +} + +/// Create the efficiency workspace by combining single spectra workspaces into +/// one. +/// @param labels :: Axis labels for each workspace. +/// @param workspaces :: Workspaces to put together. +MatrixWorkspace_sptr JoinISISPolarizationEfficiencies::createEfficiencies( + std::vector<std::string> const &labels, + std::vector<MatrixWorkspace_sptr> const &workspaces) { + auto interpolatedWorkspaces = interpolateWorkspaces(workspaces); + + auto const &inWS = interpolatedWorkspaces.front(); + MatrixWorkspace_sptr outWS = DataObjects::create<Workspace2D>( + *inWS, labels.size(), inWS->histogram(0)); + auto axis1 = new TextAxis(labels.size()); + outWS->replaceAxis(1, axis1); + outWS->getAxis(0)->setUnit("Wavelength"); + + for (size_t i = 0; i < interpolatedWorkspaces.size(); ++i) { + auto &ws = interpolatedWorkspaces[i]; + outWS->mutableX(i) = ws->x(0); + outWS->mutableY(i) = ws->y(0); + outWS->mutableE(i) = ws->e(0); + axis1->setLabel(i, labels[i]); + } + + return outWS; +} + +/// Interpolate the workspaces so that all have the same blocksize. +/// @param workspaces :: The workspaces to interpolate. +/// @return A list of interpolated workspaces. +std::vector<MatrixWorkspace_sptr> +JoinISISPolarizationEfficiencies::interpolateWorkspaces( + std::vector<MatrixWorkspace_sptr> const &workspaces) { + size_t minSize(std::numeric_limits<size_t>::max()); + size_t maxSize(0); + bool thereAreHistograms = false; + bool allAreHistograms = true; + + // Find out if the workspaces need to be interpolated. + for (auto const &ws : workspaces) { + auto size = ws->blocksize(); + if (size < minSize) { + minSize = size; + } + if (size > maxSize) { + maxSize = size; + } + thereAreHistograms = thereAreHistograms || ws->isHistogramData(); + allAreHistograms = allAreHistograms && ws->isHistogramData(); + } + + if (thereAreHistograms != allAreHistograms) { + throw std::invalid_argument("Cannot mix histograms and point data."); + } + + // All same size, same type - nothing to do + if (minSize == maxSize) { + return workspaces; + } + + // Interpolate those that need interpolating + std::vector<MatrixWorkspace_sptr> interpolatedWorkspaces; + for (auto const &ws : workspaces) { + if (ws->blocksize() < maxSize) { + if (allAreHistograms) { + interpolatedWorkspaces.push_back( + interpolateHistogramWorkspace(ws, maxSize)); + } else { + interpolatedWorkspaces.push_back( + interpolatePointDataWorkspace(ws, maxSize)); + } + } else { + interpolatedWorkspaces.push_back(ws); + } + } + + return interpolatedWorkspaces; +} + +MatrixWorkspace_sptr +JoinISISPolarizationEfficiencies::interpolatePointDataWorkspace( + MatrixWorkspace_sptr ws, size_t const maxSize) { + auto const &x = ws->x(0); + auto const startX = x.front(); + auto const endX = x.back(); + Counts yVals(maxSize, 0.0); + auto const dX = (endX - startX) / double(maxSize - 1); + Points xVals(maxSize, LinearGenerator(startX, dX)); + auto newHisto = Histogram(xVals, yVals); + interpolateLinearInplace(ws->histogram(0), newHisto); + auto interpolatedWS = boost::make_shared<Workspace2D>(); + interpolatedWS->initialize(1, newHisto); + assert(interpolatedWS->y(0).size() == maxSize); + return interpolatedWS; +} + +MatrixWorkspace_sptr +JoinISISPolarizationEfficiencies::interpolateHistogramWorkspace( + MatrixWorkspace_sptr ws, size_t const maxSize) { + ws->setDistribution(true); + auto const &x = ws->x(0); + auto const dX = (x.back() - x.front()) / double(maxSize); + std::vector<double> params(2 * maxSize + 1); + for (size_t i = 0; i < maxSize; ++i) { + params[2 * i] = x.front() + dX * double(i); + params[2 * i + 1] = dX; + } + params.back() = x.back(); + auto alg = createChildAlgorithm("InterpolatingRebin"); + alg->setProperty("InputWorkspace", ws); + alg->setProperty("Params", params); + alg->setProperty("OutputWorkspace", "dummy"); + alg->execute(); + MatrixWorkspace_sptr interpolatedWS = alg->getProperty("OutputWorkspace"); + assert(interpolatedWS->y(0).size() == maxSize); + assert(interpolatedWS->x(0).size() == maxSize + 1); + return interpolatedWS; +} + +} // namespace DataHandling +} // namespace Mantid diff --git a/Framework/DataHandling/src/LoadAscii2.cpp b/Framework/DataHandling/src/LoadAscii2.cpp index 16742656887350eaeb14eddd395f522e0d55ef66..304cab3f647a97e8cf6b7883a7d8fabe09faa0cc 100644 --- a/Framework/DataHandling/src/LoadAscii2.cpp +++ b/Framework/DataHandling/src/LoadAscii2.cpp @@ -107,16 +107,20 @@ API::Workspace_sptr LoadAscii2::readData(std::ifstream &file) { try { localWorkspace = WorkspaceFactory::Instance().create( "Workspace2D", numSpectra, m_lastBins, m_lastBins); - } catch (std::exception &) { - throw std::runtime_error("Failed to create a Workspace2D from the " - "data found in this file"); + } catch (std::exception &e) { + std::ostringstream msg; + msg << "Failed to create a Workspace2D from the data found in this file. " + "Error: " << e.what(); + throw std::runtime_error(msg.str()); } try { writeToWorkspace(localWorkspace, numSpectra); - } catch (std::exception &) { - throw std::runtime_error("Failed to write read data into the " - "output Workspace2D"); + } catch (std::exception &e) { + std::ostringstream msg; + msg << "Failed to write read data into the output Workspace2D. Error: " + << e.what(); + throw std::runtime_error(msg.str()); } delete m_curSpectra; return localWorkspace; diff --git a/Framework/DataHandling/src/LoadISISPolarizationEfficiencies.cpp b/Framework/DataHandling/src/LoadISISPolarizationEfficiencies.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d5e0b494693b8def90e49b702ebf410d93f0c34 --- /dev/null +++ b/Framework/DataHandling/src/LoadISISPolarizationEfficiencies.cpp @@ -0,0 +1,123 @@ +#include "MantidDataHandling/LoadISISPolarizationEfficiencies.h" + +#include "MantidAPI/FileProperty.h" +#include "MantidAPI/TextAxis.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidHistogramData/Histogram.h" +#include "MantidHistogramData/Interpolate.h" +#include "MantidKernel/make_unique.h" +#include <limits> + +namespace Mantid { +namespace DataHandling { + +using namespace Mantid::Kernel; +using namespace Mantid::API; + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(LoadISISPolarizationEfficiencies) + +//---------------------------------------------------------------------------------------------- + +/// Algorithms name for identification. @see Algorithm::name +const std::string LoadISISPolarizationEfficiencies::name() const { + return "LoadISISPolarizationEfficiencies"; +} + +/// Algorithm's version for identification. @see Algorithm::version +int LoadISISPolarizationEfficiencies::version() const { return 1; } + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string LoadISISPolarizationEfficiencies::summary() const { + return "Loads ISIS reflectometry polarization efficiency factors from files: " + "one factor per file."; +} + +const std::vector<std::string> +LoadISISPolarizationEfficiencies::seeAlso() const { + return {"CreatePolarizationEfficiencies", "JoinISISPolarizationEfficiencies", + "PolarizationEfficiencyCor"}; +} + +//---------------------------------------------------------------------------------------------- +/** Initialize the algorithm's properties. + */ +void LoadISISPolarizationEfficiencies::init() { + declareProperty(Kernel::make_unique<API::FileProperty>( + Pp, "", API::FileProperty::OptionalLoad), + "Path to the file containing the Pp polarization efficiency " + "in XYE columns."); + + declareProperty(Kernel::make_unique<API::FileProperty>( + Ap, "", API::FileProperty::OptionalLoad), + "Path to the file containing the Ap polarization efficiency " + "in XYE columns."); + + declareProperty(Kernel::make_unique<API::FileProperty>( + Rho, "", API::FileProperty::OptionalLoad), + "Path to the file containing the Rho polarization efficiency " + "in XYE columns."); + + declareProperty( + Kernel::make_unique<API::FileProperty>(Alpha, "", + API::FileProperty::OptionalLoad), + "Path to the file containing the Alpha polarization efficiency " + "in XYE columns."); + + declareProperty(Kernel::make_unique<API::FileProperty>( + P1, "", API::FileProperty::OptionalLoad), + "Path to the file containing the P1 polarization efficiency " + "in XYE columns."); + + declareProperty(Kernel::make_unique<API::FileProperty>( + P2, "", API::FileProperty::OptionalLoad), + "Path to the file containing the P2 polarization efficiency " + "in XYE columns."); + + declareProperty(Kernel::make_unique<API::FileProperty>( + F1, "", API::FileProperty::OptionalLoad), + "Path to the file containing the F1 polarization efficiency " + "in XYE columns."); + + declareProperty(Kernel::make_unique<API::FileProperty>( + F2, "", API::FileProperty::OptionalLoad), + "Path to the file containing the F2 polarization efficiency " + "in XYE columns."); + + initOutputWorkspace(); +} + +/// Load efficiencies from files and put them into a single workspace. +/// @param props :: Names of properties containg names of files to load. +MatrixWorkspace_sptr LoadISISPolarizationEfficiencies::createEfficiencies( + std::vector<std::string> const &props) { + + auto alg = createChildAlgorithm("JoinISISPolarizationEfficiencies"); + alg->initialize(); + for (auto const &propName : props) { + auto loader = createChildAlgorithm("Load"); + loader->initialize(); + loader->setPropertyValue("Filename", getPropertyValue(propName)); + loader->execute(); + Workspace_sptr output = loader->getProperty("OutputWorkspace"); + auto ws = boost::dynamic_pointer_cast<MatrixWorkspace>(output); + if (!ws) { + throw std::invalid_argument("File " + propName + + " cannot be loaded into a MatrixWorkspace."); + } + if (ws->getNumberHistograms() != 1) { + throw std::runtime_error( + "Loaded workspace must contain a single histogram. Found " + + std::to_string(ws->getNumberHistograms())); + } + alg->setProperty(propName, ws); + } + alg->execute(); + MatrixWorkspace_sptr outWS = alg->getProperty("OutputWorkspace"); + return outWS; +} + +} // namespace DataHandling +} // namespace Mantid diff --git a/Framework/DataHandling/test/CreatePolarizationEfficienciesTest.h b/Framework/DataHandling/test/CreatePolarizationEfficienciesTest.h new file mode 100644 index 0000000000000000000000000000000000000000..d7ba71c5edb28413e11b6cf790da3573b1e24401 --- /dev/null +++ b/Framework/DataHandling/test/CreatePolarizationEfficienciesTest.h @@ -0,0 +1,321 @@ +#ifndef MANTID_ALGORITHMS_CREATEPOLARIZATIONEFFICIENCIES_TEST_H_ +#define MANTID_ALGORITHMS_CREATEPOLARIZATIONEFFICIENCIES_TEST_H_ + +#include <cxxtest/TestSuite.h> +#include "MantidDataHandling/CreatePolarizationEfficiencies.h" +#include "MantidAPI/Axis.h" +#include "MantidKernel/Unit.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidHistogramData/LinearGenerator.h" +#include "MantidTestHelpers/WorkspaceCreationHelper.h" + +#include <boost/make_shared.hpp> + +using namespace Mantid::API; +using namespace Mantid::DataHandling; +using namespace Mantid::DataObjects; +using namespace Mantid::HistogramData; +using namespace WorkspaceCreationHelper; + +class CreatePolarizationEfficienciesTest : 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 CreatePolarizationEfficienciesTest *createSuite() { + return new CreatePolarizationEfficienciesTest(); + } + static void destroySuite(CreatePolarizationEfficienciesTest *suite) { + delete suite; + } + + void test_init() { + CreatePolarizationEfficiencies alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + } + + void test_no_input() { + auto inWS = createPointWS(); + CreatePolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", inWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + + void test_mixed_input() { + auto inWS = createHistoWS(); + + CreatePolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", inWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setPropertyValue("Pp", "1,0,0,0"); + alg.setPropertyValue("Ap", "0,1,0,0"); + alg.setPropertyValue("F1", "0,0,1,0"); + alg.setPropertyValue("F2", "0,0,0,1"); + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + + void test_histo() { + auto inWS = createHistoWS(); + + CreatePolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", inWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setPropertyValue("Pp", "1,0,0,0"); + alg.setPropertyValue("Ap", "0,1,0,0"); + alg.setPropertyValue("Rho", "0,0,1,0"); + alg.setPropertyValue("Alpha", "0,0,0,1"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "Pp"); + TS_ASSERT_EQUALS(axis1->label(1), "Ap"); + TS_ASSERT_EQUALS(axis1->label(2), "Rho"); + TS_ASSERT_EQUALS(axis1->label(3), "Alpha"); + + TS_ASSERT_DELTA(outWS->readY(0)[0], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[1], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[2], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[3], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[4], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[5], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[6], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[7], 1.0, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(1)[0], 0.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[1], 0.75, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[2], 1.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[3], 1.75, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[4], 2.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[5], 2.75, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[6], 3.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[7], 3.75, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(2)[0], 0.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[1], 0.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[2], 1.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[3], 3.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[4], 5.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[5], 7.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[6], 10.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[7], 14.0625, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(3)[0], 0.015625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[1], 0.421875, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[2], 1.953125, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[3], 5.359375, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[4], 11.390625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[5], 20.796875, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[6], 34.328125, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[7], 52.734375, 1e-15); + } + + void test_histo_partial() { + auto inWS = createHistoWS(); + + CreatePolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", inWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setPropertyValue("Pp", "1,0,0,0"); + alg.setPropertyValue("Rho", "0,0,1,0"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 2); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "Pp"); + TS_ASSERT_EQUALS(axis1->label(1), "Rho"); + + TS_ASSERT_DELTA(outWS->readY(0)[0], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[1], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[2], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[3], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[4], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[5], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[6], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[7], 1.0, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(1)[0], 0.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[1], 0.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[2], 1.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[3], 3.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[4], 5.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[5], 7.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[6], 10.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[7], 14.0625, 1e-15); + } + + void test_points() { + auto inWS = createPointWS(); + + CreatePolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", inWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setPropertyValue("Pp", "1,0,0,0"); + alg.setPropertyValue("Ap", "0,1,0,0"); + alg.setPropertyValue("Rho", "0,0,1,0"); + alg.setPropertyValue("Alpha", "0,0,0,1"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "Pp"); + TS_ASSERT_EQUALS(axis1->label(1), "Ap"); + TS_ASSERT_EQUALS(axis1->label(2), "Rho"); + TS_ASSERT_EQUALS(axis1->label(3), "Alpha"); + + TS_ASSERT_DELTA(outWS->readY(0)[0], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[1], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[2], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[3], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[4], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[5], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[6], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[7], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[8], 1.0, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(1)[0], 0.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[1], 0.5, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[2], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[3], 1.5, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[4], 2.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[5], 2.5, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[6], 3.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[7], 3.5, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[8], 4.0, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(2)[0], 0.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[1], 0.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[2], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[3], 2.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[4], 4.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[5], 6.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[6], 9.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[7], 12.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[8], 16.0, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(3)[0], 0.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[1], 0.125, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[2], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[3], 3.375, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[4], 8.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[5], 15.625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[6], 27.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[7], 42.875, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[8], 64.0, 1e-15); + } + + void test_histo_wildes() { + auto inWS = createHistoWS(); + + CreatePolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("InputWorkspace", inWS); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setPropertyValue("P1", "1,0,0,0"); + alg.setPropertyValue("P2", "0,1,0,0"); + alg.setPropertyValue("F1", "0,0,1,0"); + alg.setPropertyValue("F2", "0,0,0,1"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "P1"); + TS_ASSERT_EQUALS(axis1->label(1), "P2"); + TS_ASSERT_EQUALS(axis1->label(2), "F1"); + TS_ASSERT_EQUALS(axis1->label(3), "F2"); + + TS_ASSERT_DELTA(outWS->readY(0)[0], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[1], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[2], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[3], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[4], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[5], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[6], 1.0, 1e-15); + TS_ASSERT_DELTA(outWS->readY(0)[7], 1.0, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(1)[0], 0.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[1], 0.75, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[2], 1.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[3], 1.75, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[4], 2.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[5], 2.75, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[6], 3.25, 1e-15); + TS_ASSERT_DELTA(outWS->readY(1)[7], 3.75, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(2)[0], 0.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[1], 0.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[2], 1.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[3], 3.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[4], 5.0625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[5], 7.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[6], 10.5625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(2)[7], 14.0625, 1e-15); + + TS_ASSERT_DELTA(outWS->readY(3)[0], 0.015625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[1], 0.421875, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[2], 1.953125, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[3], 5.359375, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[4], 11.390625, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[5], 20.796875, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[6], 34.328125, 1e-15); + TS_ASSERT_DELTA(outWS->readY(3)[7], 52.734375, 1e-15); + } + +private: + Workspace2D_sptr createHistoWS() { + size_t const size = 8; + BinEdges xVals(size + 1, LinearGenerator(0, 0.5)); + Counts yVals(size, 0); + auto retVal = boost::make_shared<Workspace2D>(); + retVal->initialize(1, Histogram(xVals, yVals)); + retVal->getAxis(0)->setUnit("Wavelength"); + return retVal; + } + + Workspace2D_sptr createPointWS() { + size_t const size = 9; + Points xVals(size, LinearGenerator(0, 0.5)); + Counts yVals(size, 0); + auto retVal = boost::make_shared<Workspace2D>(); + retVal->initialize(1, Histogram(xVals, yVals)); + retVal->getAxis(0)->setUnit("Wavelength"); + return retVal; + } +}; + +#endif /* MANTID_ALGORITHMS_CREATEPOLARIZATIONEFFICIENCIES_TEST_H_ */ diff --git a/Framework/DataHandling/test/JoinISISPolarizationEfficienciesTest.h b/Framework/DataHandling/test/JoinISISPolarizationEfficienciesTest.h new file mode 100644 index 0000000000000000000000000000000000000000..02d3e7711970f541261a48902e9cc4f0bc80172f --- /dev/null +++ b/Framework/DataHandling/test/JoinISISPolarizationEfficienciesTest.h @@ -0,0 +1,596 @@ +#ifndef MANTID_DATAHANDLING_JOINISISPOLARIZATIONEFFICIENCIESTEST_H_ +#define MANTID_DATAHANDLING_JOINISISPOLARIZATIONEFFICIENCIESTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidDataHandling/JoinISISPolarizationEfficiencies.h" + +#include "MantidAPI/Axis.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidHistogramData/BinEdges.h" +#include "MantidHistogramData/Counts.h" +#include "MantidHistogramData/LinearGenerator.h" +#include "MantidKernel/Unit.h" + +#include <array> +#include <numeric> + +using Mantid::DataHandling::JoinISISPolarizationEfficiencies; +using namespace Mantid::API; +using namespace Mantid::DataObjects; +using namespace Mantid::HistogramData; + +class JoinISISPolarizationEfficienciesTest : 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 JoinISISPolarizationEfficienciesTest *createSuite() { + return new JoinISISPolarizationEfficienciesTest(); + } + static void destroySuite(JoinISISPolarizationEfficienciesTest *suite) { + delete suite; + } + + void test_initialization() { + JoinISISPolarizationEfficiencies alg; + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + } + + void test_no_input() { + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setPropertyValue("OutputWorkspace", "dummy"); + // Error: At least one of the efficiency file names must be set. + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + + void test_mixed_input() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(10, 0, 10); + auto ws3 = createHistoWS(10, 0, 10); + auto ws4 = createHistoWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("Pp", ws1); + alg.setProperty("Ap", ws2); + alg.setProperty("P1", ws3); + alg.setProperty("P2", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + // Error: Efficiencies belonging to different methods cannot mix. + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + + void test_fredrikze() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(10, 0, 10); + auto ws3 = createHistoWS(10, 0, 10); + auto ws4 = createHistoWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("Pp", ws1); + alg.setProperty("Ap", ws2); + alg.setProperty("Rho", ws3); + alg.setProperty("Alpha", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + TS_ASSERT_EQUALS(outWS->blocksize(), 10); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "Pp"); + TS_ASSERT_EQUALS(axis1->label(1), "Ap"); + TS_ASSERT_EQUALS(axis1->label(2), "Rho"); + TS_ASSERT_EQUALS(axis1->label(3), "Alpha"); + + TS_ASSERT(outWS->isHistogramData()); + + { + auto const &x = outWS->x(0); + auto const &y = outWS->y(0); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(1); + auto const &y = outWS->y(1); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(2); + auto const &y = outWS->y(2); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(3); + auto const &y = outWS->y(3); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + } + + void test_wildes() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(10, 0, 10); + auto ws3 = createHistoWS(10, 0, 10); + auto ws4 = createHistoWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("P1", ws1); + alg.setProperty("P2", ws2); + alg.setProperty("F1", ws3); + alg.setProperty("F2", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + TS_ASSERT_EQUALS(outWS->blocksize(), 10); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "P1"); + TS_ASSERT_EQUALS(axis1->label(1), "P2"); + TS_ASSERT_EQUALS(axis1->label(2), "F1"); + TS_ASSERT_EQUALS(axis1->label(3), "F2"); + + TS_ASSERT(outWS->isHistogramData()); + } + + void test_wildes_points() { + auto ws1 = createPointWS(10, 0, 10); + auto ws2 = createPointWS(10, 0, 10); + auto ws3 = createPointWS(10, 0, 10); + auto ws4 = createPointWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("P1", ws1); + alg.setProperty("P2", ws2); + alg.setProperty("F1", ws3); + alg.setProperty("F2", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + TS_ASSERT_EQUALS(outWS->blocksize(), 10); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "P1"); + TS_ASSERT_EQUALS(axis1->label(1), "P2"); + TS_ASSERT_EQUALS(axis1->label(2), "F1"); + TS_ASSERT_EQUALS(axis1->label(3), "F2"); + + TS_ASSERT(!outWS->isHistogramData()); + + { + auto const &x = outWS->x(0); + auto const &y = outWS->y(0); + TS_ASSERT_EQUALS(x.size(), 10); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + auto sum = std::accumulate(y.begin(), y.end(), 0.0); + TS_ASSERT_DELTA(sum, 10.0, 1e-14); + } + + { + auto const &x = outWS->x(1); + auto const &y = outWS->y(1); + TS_ASSERT_EQUALS(x.size(), 10); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + auto sum = std::accumulate(y.begin(), y.end(), 0.0); + TS_ASSERT_DELTA(sum, 10.0, 1e-14); + } + + { + auto const &x = outWS->x(2); + auto const &y = outWS->y(2); + TS_ASSERT_EQUALS(x.size(), 10); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + auto sum = std::accumulate(y.begin(), y.end(), 0.0); + TS_ASSERT_DELTA(sum, 10.0, 1e-14); + } + + { + auto const &x = outWS->x(3); + auto const &y = outWS->y(3); + TS_ASSERT_EQUALS(x.size(), 10); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + auto sum = std::accumulate(y.begin(), y.end(), 0.0); + TS_ASSERT_DELTA(sum, 10.0, 1e-14); + } + } + + void test_histo_3_out_of_4() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(10, 0, 10); + auto ws3 = createHistoWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("P1", ws1); + alg.setProperty("P2", ws2); + alg.setProperty("F1", ws3); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 3); + TS_ASSERT_EQUALS(outWS->blocksize(), 10); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "P1"); + TS_ASSERT_EQUALS(axis1->label(1), "P2"); + TS_ASSERT_EQUALS(axis1->label(2), "F1"); + } + + void test_histo_2_out_of_4() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("P1", ws1); + alg.setProperty("F1", ws2); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 2); + TS_ASSERT_EQUALS(outWS->blocksize(), 10); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "P1"); + TS_ASSERT_EQUALS(axis1->label(1), "F1"); + } + + void test_histo_1_out_of_4() { + auto ws1 = createHistoWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("F2", ws1); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 1); + TS_ASSERT_EQUALS(outWS->blocksize(), 10); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "F2"); + } + + void test_mixed_histo_points() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createPointWS(10, 0, 10); + auto ws3 = createHistoWS(10, 0, 10); + auto ws4 = createHistoWS(10, 0, 10); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("P1", ws1); + alg.setProperty("P2", ws2); + alg.setProperty("F1", ws3); + alg.setProperty("F2", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + // Error: Cannot mix histograms and point data. + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + + void test_ragged() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(10, 1, 10); + auto ws3 = createHistoWS(10, 2, 3); + auto ws4 = createHistoWS(10, 11, 20); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("Pp", ws1); + alg.setProperty("Ap", ws2); + alg.setProperty("Rho", ws3); + alg.setProperty("Alpha", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + TS_ASSERT_EQUALS(outWS->blocksize(), 10); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "Pp"); + TS_ASSERT_EQUALS(axis1->label(1), "Ap"); + TS_ASSERT_EQUALS(axis1->label(2), "Rho"); + TS_ASSERT_EQUALS(axis1->label(3), "Alpha"); + + TS_ASSERT(outWS->isHistogramData()); + + { + auto const &x = outWS->x(0); + auto const &y = outWS->y(0); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 0); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(1); + auto const &y = outWS->y(1); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 1); + TS_ASSERT_EQUALS(x.back(), 10); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(2); + auto const &y = outWS->y(2); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 2); + TS_ASSERT_EQUALS(x.back(), 3); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(3); + auto const &y = outWS->y(3); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 10); + TS_ASSERT_EQUALS(x.front(), 11); + TS_ASSERT_EQUALS(x.back(), 20); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + } + + void test_histo_ragged_diff_sizes() { + auto ws1 = createHistoWS(10, 0, 10); + auto ws2 = createHistoWS(9, 1, 10); + auto ws3 = createHistoWS(11, 2, 3); + auto ws4 = createHistoWS(10, 11, 20); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("Pp", ws1); + alg.setProperty("Ap", ws2); + alg.setProperty("Rho", ws3); + alg.setProperty("Alpha", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + TS_ASSERT_EQUALS(outWS->blocksize(), 11); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "Pp"); + TS_ASSERT_EQUALS(axis1->label(1), "Ap"); + TS_ASSERT_EQUALS(axis1->label(2), "Rho"); + TS_ASSERT_EQUALS(axis1->label(3), "Alpha"); + + TS_ASSERT(outWS->isHistogramData()); + + { + auto const &x = outWS->x(0); + auto const &y = outWS->y(0); + TS_ASSERT_EQUALS(x.size(), 12); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_DELTA(x.front(), 0., 1e-15); + TS_ASSERT_DELTA(x.back(), 10, 1e-15); + TS_ASSERT_DELTA(y.front(), 1., 1e-15); + TS_ASSERT_DELTA(y.back(), 1., 1e-15); + } + + { + auto const &x = outWS->x(1); + auto const &y = outWS->y(1); + TS_ASSERT_EQUALS(x.size(), 12); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_DELTA(x.front(), 1., 1e-15); + TS_ASSERT_DELTA(x.back(), 10, 1e-15); + TS_ASSERT_DELTA(y.front(), 1., 1e-15); + TS_ASSERT_DELTA(y.back(), 1., 1e-15); + } + + { + auto const &x = outWS->x(2); + auto const &y = outWS->y(2); + TS_ASSERT_EQUALS(x.size(), 12); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_DELTA(x.front(), 2.0, 1e-9); + TS_ASSERT_DELTA(x.back(), 3.0, 1e-9); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(3); + auto const &y = outWS->y(3); + TS_ASSERT_EQUALS(x.size(), 12); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_DELTA(x.front(), 11.0, 1e-15); + TS_ASSERT_DELTA(x.back(), 20.0, 1e-15); + TS_ASSERT_DELTA(y.front(), 1., 1e-15); + TS_ASSERT_DELTA(y.back(), 1., 1e-15); + } + } + + void test_points_ragged_diff_sizes() { + auto ws1 = createPointWS(10, 0, 10); + auto ws2 = createPointWS(9, 1, 10); + auto ws3 = createPointWS(11, 2, 3); + auto ws4 = createPointWS(10, 11, 20); + + JoinISISPolarizationEfficiencies alg; + alg.initialize(); + alg.setChild(true); + alg.setRethrows(true); + alg.setProperty("Pp", ws1); + alg.setProperty("Ap", ws2); + alg.setProperty("Rho", ws3); + alg.setProperty("Alpha", ws4); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 4); + TS_ASSERT_EQUALS(outWS->blocksize(), 11); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "Pp"); + TS_ASSERT_EQUALS(axis1->label(1), "Ap"); + TS_ASSERT_EQUALS(axis1->label(2), "Rho"); + TS_ASSERT_EQUALS(axis1->label(3), "Alpha"); + + TS_ASSERT(!outWS->isHistogramData()); + + { + auto const &x = outWS->x(0); + auto const &y = outWS->y(0); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_DELTA(x.front(), 0, 1e-5); + TS_ASSERT_DELTA(x.back(), 10.0, 1e-15); + TS_ASSERT_DELTA(y.front(), 1.0, 1e-15); + TS_ASSERT_DELTA(y.back(), 1.0, 1e-15); + } + + { + auto const &x = outWS->x(1); + auto const &y = outWS->y(1); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_DELTA(x.front(), 1.0, 1e-15); + TS_ASSERT_DELTA(x.back(), 10.0, 1e-15); + TS_ASSERT_DELTA(y.front(), 1.0, 1e-15); + TS_ASSERT_DELTA(y.back(), 1.0, 1e-15); + } + + { + auto const &x = outWS->x(2); + auto const &y = outWS->y(2); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_EQUALS(x.front(), 2); + TS_ASSERT_EQUALS(x.back(), 3); + TS_ASSERT_EQUALS(y.front(), 1); + TS_ASSERT_EQUALS(y.back(), 1); + } + + { + auto const &x = outWS->x(3); + auto const &y = outWS->y(3); + TS_ASSERT_EQUALS(x.size(), 11); + TS_ASSERT_EQUALS(y.size(), 11); + TS_ASSERT_DELTA(x.front(), 11.0, 1e-15); + TS_ASSERT_DELTA(x.back(), 20.0, 1e-15); + TS_ASSERT_DELTA(y.front(), 1.0, 1e-15); + TS_ASSERT_DELTA(y.back(), 1.0, 1e-15); + } + } + +private: + MatrixWorkspace_sptr createHistoWS(size_t size, double startX, + double endX) const { + double const dX = (endX - startX) / double(size); + BinEdges xVals(size + 1, LinearGenerator(startX, dX)); + Counts yVals(size, 1.0); + auto retVal = boost::make_shared<Workspace2D>(); + retVal->initialize(1, Histogram(xVals, yVals)); + return retVal; + } + + MatrixWorkspace_sptr createPointWS(size_t size, double startX, + double endX) const { + double const dX = (endX - startX) / double(size - 1); + Points xVals(size, LinearGenerator(startX, dX)); + Counts yVals(size, 1.0); + auto retVal = boost::make_shared<Workspace2D>(); + retVal->initialize(1, Histogram(xVals, yVals)); + return retVal; + } +}; + +#endif /* MANTID_DATAHANDLING_JOINISISPOLARIZATIONEFFICIENCIESTEST_H_ */ diff --git a/Framework/DataHandling/test/LoadISISPolarizationEfficienciesTest.h b/Framework/DataHandling/test/LoadISISPolarizationEfficienciesTest.h new file mode 100644 index 0000000000000000000000000000000000000000..ba3a4e47860ac9411c1e7a041f7b40a093401853 --- /dev/null +++ b/Framework/DataHandling/test/LoadISISPolarizationEfficienciesTest.h @@ -0,0 +1,163 @@ +#ifndef MANTID_DATAHANDLING_LOADISISPOLARIZATIONEFFICIENCIESTEST_H_ +#define MANTID_DATAHANDLING_LOADISISPOLARIZATIONEFFICIENCIESTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidDataHandling/LoadISISPolarizationEfficiencies.h" + +#include "MantidAPI/Axis.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceCreation.h" +#include "MantidHistogramData/BinEdges.h" +#include "MantidHistogramData/Counts.h" +#include "MantidHistogramData/LinearGenerator.h" +#include "MantidKernel/Unit.h" +#include "MantidTestHelpers/ScopedFileHelper.h" + +#include <array> +#include <fstream> + +using Mantid::DataHandling::LoadISISPolarizationEfficiencies; +using namespace Mantid::API; +using namespace Mantid::DataObjects; +using namespace Mantid::HistogramData; +using ScopedFileHelper::ScopedFile; + +class LoadISISPolarizationEfficienciesTest : 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 LoadISISPolarizationEfficienciesTest *createSuite() { + return new LoadISISPolarizationEfficienciesTest(); + } + static void destroySuite(LoadISISPolarizationEfficienciesTest *suite) { + delete suite; + } + + void test_initialization() { + LoadISISPolarizationEfficiencies alg; + alg.setRethrows(true); + TS_ASSERT_THROWS_NOTHING(alg.initialize()) + TS_ASSERT(alg.isInitialized()) + } + + void test_load() { + ScopedFile f1(m_data1, "Efficiency1.txt"); + + LoadISISPolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("P1", f1.getFileName()); + alg.setProperty("P2", f1.getFileName()); + alg.setProperty("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 2); + TS_ASSERT_EQUALS(outWS->blocksize(), 5); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "P1"); + TS_ASSERT_EQUALS(axis1->label(1), "P2"); + + TS_ASSERT(!outWS->isHistogramData()); + + { + auto const &x = outWS->x(0); + auto const &y = outWS->y(0); + TS_ASSERT_EQUALS(x.size(), 5); + TS_ASSERT_EQUALS(y.size(), 5); + TS_ASSERT_DELTA(x.front(), 1.1, 1e-15); + TS_ASSERT_DELTA(x.back(), 5.5, 1e-15); + TS_ASSERT_DELTA(y.front(), 1., 1e-15); + TS_ASSERT_DELTA(y.back(), 1., 1e-15); + } + + { + auto const &x = outWS->x(1); + auto const &y = outWS->y(1); + TS_ASSERT_EQUALS(x.size(), 5); + TS_ASSERT_EQUALS(y.size(), 5); + TS_ASSERT_DELTA(x.front(), 1.1, 1e-15); + TS_ASSERT_DELTA(x.back(), 5.5, 1e-15); + TS_ASSERT_DELTA(y.front(), 1., 1e-15); + TS_ASSERT_DELTA(y.back(), 1., 1e-15); + } + } + + void test_load_diff_sizes() { + ScopedFile f1(m_data1, "Efficiency2.txt"); + + LoadISISPolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("P1", f1.getFileName()); + alg.setProperty("P2", f1.getFileName()); + alg.setProperty("OutputWorkspace", "dummy"); + alg.execute(); + MatrixWorkspace_sptr outWS = alg.getProperty("OutputWorkspace"); + TS_ASSERT(outWS); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), 2); + TS_ASSERT_EQUALS(outWS->blocksize(), 5); + TS_ASSERT_EQUALS(outWS->getAxis(0)->unit()->caption(), "Wavelength"); + + auto axis1 = outWS->getAxis(1); + TS_ASSERT_EQUALS(axis1->label(0), "P1"); + TS_ASSERT_EQUALS(axis1->label(1), "P2"); + + TS_ASSERT(!outWS->isHistogramData()); + + { + auto const &x = outWS->x(0); + auto const &y = outWS->y(0); + TS_ASSERT_EQUALS(x.size(), 5); + TS_ASSERT_EQUALS(y.size(), 5); + TS_ASSERT_DELTA(x.front(), 1.1, 1e-15); + TS_ASSERT_DELTA(x.back(), 5.5, 1e-15); + TS_ASSERT_DELTA(y.front(), 1., 1e-15); + TS_ASSERT_DELTA(y.back(), 1., 1e-15); + } + + { + auto const &x = outWS->x(1); + auto const &y = outWS->y(1); + TS_ASSERT_EQUALS(x.size(), 5); + TS_ASSERT_EQUALS(y.size(), 5); + TS_ASSERT_DELTA(x.front(), 1.1, 1e-15); + // TS_ASSERT_DELTA(x.back(), 4.5, 1e-15); + TS_ASSERT_DELTA(y.front(), 1., 1e-15); + TS_ASSERT_DELTA(y.back(), 1., 1e-15); + } + } + + void test_diff_methods() { + ScopedFile f1(m_data1, "Efficiency3.txt"); + + LoadISISPolarizationEfficiencies alg; + alg.setChild(true); + alg.setRethrows(true); + alg.initialize(); + alg.setProperty("P1", f1.getFileName()); + alg.setProperty("Pp", f1.getFileName()); + alg.setProperty("OutputWorkspace", "dummy"); + TS_ASSERT_THROWS(alg.execute(), std::invalid_argument); + } + +private: + std::string const m_data1{"\n1.10000,1.000000,0.322961\n" + "2.20000,1.000000,0.0217908\n" + "3.30000,1.000000,0.00993287\n" + "4.50000,1.000000,0.00668106\n" + "5.50000,1.000000,0.0053833\n"}; + + std::string const m_data2{"\n1.10000,1.000000,0.322961\n" + "2.20000,1.000000,0.0217908\n" + "3.30000,1.000000,0.00993287\n" + "4.50000,1.000000,0.00668106\n"}; +}; + +#endif /* MANTID_DATAHANDLING_LOADISISPOLARIZATIONEFFICIENCIESTEST_H_ */ diff --git a/Framework/HistogramData/src/Interpolate.cpp b/Framework/HistogramData/src/Interpolate.cpp index 8a0daace69abb8619fe9753c8826f51358b61382..51c8735cdf3577fca9f88ee701045dfd00851246 100644 --- a/Framework/HistogramData/src/Interpolate.cpp +++ b/Framework/HistogramData/src/Interpolate.cpp @@ -250,7 +250,8 @@ void interpolateLinearInplace(Histogram &inOut, const size_t stepSize) { */ void interpolateLinearInplace(const Histogram &input, Histogram &output) { sanityCheck(input, output, minSizeForLinearInterpolation()); - const auto &points = input.points().rawData(); + const auto inputPoints = input.points(); + const auto &points = inputPoints.rawData(); const auto &y = input.y().rawData(); const auto &interpPoints = output.points(); auto &newY = output.mutableY(); diff --git a/docs/source/algorithms/CreatePolarizationEfficiencies-v1.rst b/docs/source/algorithms/CreatePolarizationEfficiencies-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..5a085962f61b5684bdeafeeadc53034cd5fed487 --- /dev/null +++ b/docs/source/algorithms/CreatePolarizationEfficiencies-v1.rst @@ -0,0 +1,43 @@ + +.. algorithm:: + +.. summary:: + +.. relatedalgorithms:: + +.. properties:: + +Description +----------- + +Creates a workspace in which the spectra contain the polarization efficiencies calculated from polynomial coefficients +on the x-values of the input workspace. + + +Usage +----- + +**Example** + +.. testcode:: Example + + ws = CreateWorkspace([0, 1, 2, 3, 4], [0, 0, 0, 0, 0]) + eff = CreatePolarizationEfficiencies(ws, Pp=[0, 1, 2, 3], Ap=[1, 2, 3], Rho=[3, 2, 1], Alpha=[4, 3, 2, 1]) + print(eff.getAxis(1).label(0)) + print(eff.getAxis(1).label(1)) + print(eff.getAxis(1).label(2)) + print(eff.getAxis(1).label(3)) + + +Output: + +.. testoutput:: Example + + Pp + Ap + Rho + Alpha + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/algorithms/JoinISISPolarizationEfficiencies-v1.rst b/docs/source/algorithms/JoinISISPolarizationEfficiencies-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..478a4a2603eba2df3f97ff90240bc59f13ce90ed --- /dev/null +++ b/docs/source/algorithms/JoinISISPolarizationEfficiencies-v1.rst @@ -0,0 +1,45 @@ +.. algorithm:: + +.. summary:: + +.. relatedalgorithms:: + +.. properties:: + +Description +----------- + +The inputs to this algorithm are single-spectra workspaces containing polarization efficiencies. They are combined and interpolated if +neccessary to form a valid matrix workspace. The spectra of the output workspace are labeled with the names of the corresponding +input properties. + + +Usage +----- + +.. testcode:: Example + + # Create input workspaces which can have different sizes + ws1 = CreateWorkspace([1, 2, 3], [1, 1]) + ws2 = CreateWorkspace([2, 3, 4, 5], [1, 1, 1]) + + # Combine them in a single workspace + efficiencies = JoinISISPolarizationEfficiencies(Pp=ws1, Ap=ws2) + print('Number of spectra = {}'.format(efficiencies.getNumberHistograms())) + print('Number of bins = {}'.format(efficiencies.blocksize())) + print('Label of first spectrum: {}'.format(efficiencies.getAxis(1).label(0))) + print('Label of second spectrum: {}'.format(efficiencies.getAxis(1).label(1))) + +Output: + +.. testoutput:: Example + + Number of spectra = 2 + Number of bins = 3 + Label of first spectrum: Pp + Label of second spectrum: Ap + + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/algorithms/LoadISISPolarizationEfficiencies-v1.rst b/docs/source/algorithms/LoadISISPolarizationEfficiencies-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..842706ed7fb49202bde5b734cf7a96050f61ccb6 --- /dev/null +++ b/docs/source/algorithms/LoadISISPolarizationEfficiencies-v1.rst @@ -0,0 +1,18 @@ +.. algorithm:: + +.. summary:: + +.. relatedalgorithms:: + +.. properties:: + +Description +----------- + +This algorithm is similar to :ref:`algm-JoinISISPolarizationEfficiencies` only the input efficiencies are taken from files +instead of workspaces. + + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/algorithms/PolarizationCorrection-v1.rst b/docs/source/algorithms/PolarizationCorrectionFredrikze-v1.rst similarity index 99% rename from docs/source/algorithms/PolarizationCorrection-v1.rst rename to docs/source/algorithms/PolarizationCorrectionFredrikze-v1.rst index c42f2de3d7fec485a9e788ea88186a38b37e00d4..8ed2fae2876cf497903f56d09707bb6312fbcddf 100644 --- a/docs/source/algorithms/PolarizationCorrection-v1.rst +++ b/docs/source/algorithms/PolarizationCorrectionFredrikze-v1.rst @@ -9,6 +9,7 @@ Description ----------- + Performs wavelength polarization correction on a TOF reflectometer spectrometer. Algorithm is based on the the paper Fredrikze, H, et al. "Calibration of a polarized neutron reflectometer" Physica B 297 (2001). diff --git a/docs/source/algorithms/PolarizationCorrectionWildes-v1.rst b/docs/source/algorithms/PolarizationCorrectionWildes-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..3d6d7b431fc16c0769dcbc521cba71838d6c752a --- /dev/null +++ b/docs/source/algorithms/PolarizationCorrectionWildes-v1.rst @@ -0,0 +1,159 @@ + +.. algorithm:: + +.. summary:: + +.. properties:: + +Description +----------- + + +This algorithm corrects for non-ideal instrument component efficiencies in a polarization analysis experiment by following the procedure and conventions introduced by Wildes [#WILDES]_. In the full polarization analysis case it solves the corrected count rates :math:`\Sigma^{++}`, :math:`\Sigma^{+-}`, :math:`\Sigma^{-+}` and :math:`\Sigma^{--}` from the equation + +.. math:: + \begin{bmatrix} + \Sigma^{++} \\ + \Sigma^{+-} \\ + \Sigma^{-+} \\ + \Sigma^{--} + \end{bmatrix} + = \bm{M} + \begin{bmatrix} + I^{00} \\ + I^{01} \\ + I^{10} \\ + I^{11} + \end{bmatrix}, + +where :math:`I^{jk}` are the experimental count rates for flipper configuration :math:`jk` and :math:`\bm{M}` is the four-by-four correction matrix as defined by equations (4) in [#WILDES]_. + +Flipper configurations +###################### + +*InputWorkspaces* is a list containing one to four workspace names (X unit: wavelength) corresponding to the instrument configurations given as *Flippers*. Supported configurations are: + +:literal:`'00, 01, 10, 11'` + Full polarization corrections. Four input workspaces are required. They should be in the input group in the following order: both flippers off, analyzer flipper on, polarizer flipper on, both flippers on. + +:literal:`'00, 01, 11'` and :literal:`'00, 10, 11'` + Polarization corrections with the assumption that the corrected count rates :math:`\Sigma^{+-} = \Sigma^{-+}`. In this case the intensity of the missing flipper configuration (01 or 10) can be solved from the other intensities. Workspaces in the input group should be in the following order: both flippers off, one flipper on, both flippers on. + +:literal:`'00, 11'` + Polarization corrections with the assumption that the corrected count rates :math:`\Sigma^{+-} = \Sigma^{-+} = 0`. In this case the intensities of the missing flipper configurations (01 and 11) can be solved from the other intensities. Workspaces in the input group should be in the following order: both flippers off, both flippers on. + +:literal:`'0, 1'` + Polarization corrections when no analyzer has been used. Workspaces in the input group should be in the following order: polarizer flipper off, polarizer flipper on. + +:literal:`'0'` + Polarization corrections for a direct beam measurement in a reflectometry experiment. + +Output +###### + +The algorithm's output is a group workspace containing the corrected workspaces. The names of each corrected workspace is prefixed by :literal:`_++`, :literal:`_+-`, :literal:`_-+` or :literal:`_--` depending on which :math:`\Sigma^{mn}` they correspond to. + +Efficiency factors +################## + +The *Efficiencies* input property expects to get a workspace with the following properties: + +* Contains four histograms, each labeled by their vertical axis as :literal:`P1`, :literal:`P2`, :literal:`F1`, :literal:`F2`. Other histograms (if present) are ignored. +* The Y values of each histogram should be the corresponding efficiencies as functions of wavelength as defined in [#WILDES]_. +* The wavelength values (X values) should be the same is in the input workspaces. + +.. note:: + Users at ILL can load a conforming efficiency workspace from disk by :ref:`algm-LoadILLPolarizationFactors`. + +Error propagation +################# + +.. note:: + Errors are calculated as per Wildes [#WILDES]_, except for the numerically solved intensity in :literal:`'00, 01, 11'` and :literal:`'00, 10, 11'` flipper configurations in which case the uncertainties of :math:`\Sigma^{+-}` or :math:`\Sigma^{-+}` are set to zero. + +Usage +----- + +.. include:: ../usagedata-note.txt + +**Example - PolarizationEfficiencyCor** + +.. testcode:: PolarizationEfficiencyCorExample + + LoadILLReflectometry( + Filename='ILL/D17/317370.nxs', + OutputWorkspace='direct_beam', + OutputBeamPosition='direct_beam_position', + XUnit='TimeOfFlight') + LoadILLReflectometry( + Filename='ILL/D17/317370.nxs', + OutputWorkspace='reflected_beam', + DirectBeamPosition='direct_beam_position', + XUnit='TimeOfFlight') + # Sum pixels containing the reflected intensity + GroupDetectors( + InputWorkspace='reflected_beam', + OutputWorkspace='reflected_beam', + WorkspaceIndexList=[199, 200, 201, 202, 203, 204, 205]) + ConvertUnits( + InputWorkspace='reflected_beam', + OutputWorkspace='reflected_beam', + Target='Wavelength', + EMode='Elastic') + # There are some unphysical wavelengths + CropWorkspace( + InputWorkspace='reflected_beam', + OutputWorkspace='reflected_beam', + XMin=0.) + # Fake two flipper configurations + RenameWorkspace( + InputWorkspace='reflected_beam', + OutputWorkspace='up' + ) + CloneWorkspace( + InputWorkspace='up', + OutputWorkspace='down' + ) + Scale( + InputWorkspace='down', + OutputWorkspace='down', + Factor=0.1 + ) + LoadILLPolarizationFactors( + Filename='ILL/D17/PolarizationFactors.txt', + OutputWorkspace='efficiencies', + WavelengthReference='up') + PolarizationEfficiencyCor( + InputWorkspaces='up, down', + OutputWorkspace='corrected', + Efficiencies='efficiencies', + Flippers='00, 11') + + orig = mtd['up'] + corr = mtd['corrected_++'] + index = orig.binIndexOf(15.) + ratio_up = corr.readY(0)[index] / orig.readY(0)[index] + print("Ratio of corrected and original 'up' intensity at 15A: {:.4}".format(ratio_up)) + orig = mtd['down'] + corr = mtd['corrected_--'] + index = orig.binIndexOf(15.) + ratio_down = corr.readY(0)[index] / orig.readY(0)[index] + print("Ratio of corrected and original 'down' intensity at 15A: {:.4}".format(ratio_down)) + +Output: + +.. testoutput:: PolarizationEfficiencyCorExample + + Ratio of corrected and original 'up' intensity at 15A: 1.038 + Ratio of corrected and original 'down' intensity at 15A: 1.062 + +References +---------- + +.. [#WILDES] A. R. Wildes, *Neutron News*, **17** 17 (2006) + `doi: 10.1080/10448630600668738 <https://doi.org/10.1080/10448630600668738>`_ + +.. categories:: + +.. sourcelink:: + diff --git a/docs/source/algorithms/PolarizationEfficiencyCor-v1.rst b/docs/source/algorithms/PolarizationEfficiencyCor-v1.rst index a34480a2307b1035f6ee503f4b5b352f272d11bd..69145706df83f57fa86cda91088239b8c5fbcd61 100644 --- a/docs/source/algorithms/PolarizationEfficiencyCor-v1.rst +++ b/docs/source/algorithms/PolarizationEfficiencyCor-v1.rst @@ -1,4 +1,3 @@ - .. algorithm:: .. summary:: @@ -10,151 +9,16 @@ Description ----------- -This algorithm corrects for non-ideal instrument component efficiencies in a polarization analysis experiment by following the procedure and conventions introduced by Wildes [#WILDES]_. In the full polarization analysis case it solves the corrected count rates :math:`\Sigma^{++}`, :math:`\Sigma^{+-}`, :math:`\Sigma^{-+}` and :math:`\Sigma^{--}` from the equation - -.. math:: - \begin{bmatrix} - \Sigma^{++} \\ - \Sigma^{+-} \\ - \Sigma^{-+} \\ - \Sigma^{--} - \end{bmatrix} - = \bm{M} - \begin{bmatrix} - I^{00} \\ - I^{01} \\ - I^{10} \\ - I^{11} - \end{bmatrix}, - -where :math:`I^{jk}` are the experimental count rates for flipper configuration :math:`jk` and :math:`\bm{M}` is the four-by-four correction matrix as defined by equations (4) in [#WILDES]_. - -Flipper configurations -###################### - -*InputWorkspaces* is a list containing one to four workspace names (X unit: wavelength) corresponding to the instrument configurations given as *Flippers*. Supported configurations are: - -:literal:`'00, 01, 10, 11'` - Full polarization corrections. Four input workspaces are required. They should be in the input group in the following order: both flippers off, polarizer flipper on, analyzer flipper on, both flippers on. - -:literal:`'00, 01, 11'` and :literal:`'00, 10, 11'` - Polarization corrections with the assumption that the corrected count rates :math:`\Sigma^{+-} = \Sigma^{-+}`. In this case the intensity of the missing flipper configuration (01 or 10) can be solved from the other intensities. Workspaces in the input group should be in the following order: both flippers off, one flipper on, both flippers on. - -:literal:`'00, 11'` - Polarization corrections with the assumption that the corrected count rates :math:`\Sigma^{+-} = \Sigma^{-+} = 0`. In this case the intensities of the missing flipper configurations (01 and 11) can be solved from the other intensities. Workspaces in the input group should be in the following order: both flippers off, both flippers on. - -:literal:`'0, 1'` - Polarization corrections when no analyzer has been used. Workspaces in the input group should be in the following order: polarizer flipper off, polarizer flipper on. - -:literal:`'0'` - Polarization corrections for a direct beam measurement in a reflectometry experiment. - -Output -###### - -The algorithm's output is a group workspace containing the corrected workspaces. The names of each corrected workspace is prefixed by :literal:`_++`, :literal:`_+-`, :literal:`_-+` or :literal:`_--` depending on which :math:`\Sigma^{mn}` they correspond to. - -Efficiency factors -################## - -The *Efficiencies* input property expects to get a workspace with the following properties: +This is a wrapper around the algorithms +:ref:`algm-PolarizationCorrectionWildes` and :ref:`algm-PolarizationCorrectionFredrikze`. Use `CorrectionMethod` property +to select between the two. The default is Wildes. -* Contains four histograms, each labeled by their vertical axis as :literal:`P1`, :literal:`P2`, :literal:`F1`, :literal:`F2`. Other histograms (if present) are ignored. -* The Y values of each histogram should be the corresponding efficiencies as functions of wavelength as defined in [#WILDES]_. -* The wavelength values (X values) should be the same is in the input workspaces. +The input workspaces can be passed in either via `InputWorkspaces` or +`InputWorkspaceGroup` property but not both. An attempt to set both properties will result in an error. -.. note:: - Users at ILL can load a conforming efficiency workspace from disk by :ref:`algm-LoadILLPolarizationFactors`. - -Error propagation -################# - -.. note:: - Errors are calculated as per Wildes [#WILDES]_, except for the numerically solved intensity in :literal:`'00, 01, 11'` and :literal:`'00, 10, 11'` flipper configurations in which case the uncertainties of :math:`\Sigma^{+-}` or :math:`\Sigma^{-+}` are set to zero. - -Usage ------ - -.. include:: ../usagedata-note.txt - -**Example - PolarizationEfficiencyCor** - -.. testcode:: PolarizationEfficiencyCorExample - - LoadILLReflectometry( - Filename='ILL/D17/317370.nxs', - OutputWorkspace='direct_beam', - OutputBeamPosition='direct_beam_position', - XUnit='TimeOfFlight') - LoadILLReflectometry( - Filename='ILL/D17/317370.nxs', - OutputWorkspace='reflected_beam', - DirectBeamPosition='direct_beam_position', - XUnit='TimeOfFlight') - # Sum pixels containing the reflected intensity - GroupDetectors( - InputWorkspace='reflected_beam', - OutputWorkspace='reflected_beam', - WorkspaceIndexList=[199, 200, 201, 202, 203, 204, 205]) - ConvertUnits( - InputWorkspace='reflected_beam', - OutputWorkspace='reflected_beam', - Target='Wavelength', - EMode='Elastic') - # There are some unphysical wavelengths - CropWorkspace( - InputWorkspace='reflected_beam', - OutputWorkspace='reflected_beam', - XMin=0.) - # Fake two flipper configurations - RenameWorkspace( - InputWorkspace='reflected_beam', - OutputWorkspace='up' - ) - CloneWorkspace( - InputWorkspace='up', - OutputWorkspace='down' - ) - Scale( - InputWorkspace='down', - OutputWorkspace='down', - Factor=0.1 - ) - LoadILLPolarizationFactors( - Filename='ILL/D17/PolarizationFactors.txt', - OutputWorkspace='efficiencies', - WavelengthReference='up') - PolarizationEfficiencyCor( - InputWorkspaces='up, down', - OutputWorkspace='corrected', - Efficiencies='efficiencies', - Flippers='00, 11') - - orig = mtd['up'] - corr = mtd['corrected_++'] - index = orig.binIndexOf(15.) - ratio_up = corr.readY(0)[index] / orig.readY(0)[index] - print("Ratio of corrected and original 'up' intensity at 15A: {:.4}".format(ratio_up)) - orig = mtd['down'] - corr = mtd['corrected_--'] - index = orig.binIndexOf(15.) - ratio_down = corr.readY(0)[index] / orig.readY(0)[index] - print("Ratio of corrected and original 'down' intensity at 15A: {:.4}".format(ratio_down)) - -Output: - -.. testoutput:: PolarizationEfficiencyCorExample - - Ratio of corrected and original 'up' intensity at 15A: 1.038 - Ratio of corrected and original 'down' intensity at 15A: 1.062 - -References ----------- - -.. [#WILDES] A. R. Wildes, *Neutron News*, **17** 17 (2006) - `doi: 10.1080/10448630600668738 <https://doi.org/10.1080/10448630600668738>`_ +The default values for the `Flippers` and `PolarizationAnalysis` properties are empty strings and correspond to the actual +defaults of the child algorithms: `00, 01, 10, 11` for Wildes and `PA` for Fredrikze. .. categories:: .. sourcelink:: - diff --git a/docs/source/algorithms/ReflectometryReductionOneAuto-v2.rst b/docs/source/algorithms/ReflectometryReductionOneAuto-v2.rst index 4258f0d63cb608753b0818403fce8e1ca991567d..0156eaeb847e33df16fd1fd792e02c90b3c2a387 100644 --- a/docs/source/algorithms/ReflectometryReductionOneAuto-v2.rst +++ b/docs/source/algorithms/ReflectometryReductionOneAuto-v2.rst @@ -106,7 +106,7 @@ Polarization Analysis On If :literal:`PolarizationAnalysis` is set to :literal:`PA` or :literal:`PNR` the reduction continues and polarization corrections will be applied to the output workspace in wavelength. The algorithm will use the properties :literal:`PolarizationAnalysis`, -:literal:`CPp`, :literal:`CAp`, :literal:`CRho` and :literal:`CAlpha` to run :ref:`algm-PolarizationCorrection`. +:literal:`Pp`, :literal:`Ap`, :literal:`Rho` and :literal:`Alpha` to run :ref:`algm-PolarizationCorrectionFredrikze`. The result will be a new workspace in wavelength, which will override the previous one, that will be used as input to :ref:`algm-ReflectometryReductionOne` to calculate the new output workspaces in Q, which in turn will override the existing workspaces in Q. Note that if transmission runs are provided in the form of workspace diff --git a/docs/source/interfaces/ISIS Reflectometry.rst b/docs/source/interfaces/ISIS Reflectometry.rst index 59adf74a69c70886419c5d9382cfceea8c759c60..bd8bfe4b1aecec8ce2faa5b932c1cb14c762b5d1 100644 --- a/docs/source/interfaces/ISIS Reflectometry.rst +++ b/docs/source/interfaces/ISIS Reflectometry.rst @@ -1,5 +1,6 @@ .. _interface-isis-refl: + ============================ ISIS Reflectometry Interface ============================ diff --git a/docs/source/release/v3.13.0/reflectometry.rst b/docs/source/release/v3.13.0/reflectometry.rst index c3c757687314402d038c6eae4e35573c7574b747..72d1b5f7f5cc48b82dcb3f14e75d2ee0568665e3 100644 --- a/docs/source/release/v3.13.0/reflectometry.rst +++ b/docs/source/release/v3.13.0/reflectometry.rst @@ -30,16 +30,23 @@ Algorithms ---------- * Removed version 1 of ``ReflectometryReductionOne`` and ``ReflectometryReductionOneAuto``. +* Renamed algorithms ``PolarizationCorrection`` to ``PolarizationCorrectionFredrikze`` and ``PolarizationEfficiencyCor`` to ``PolarizationCorrectionWildes``. New features ############ -- Algorithms for reflectometry reduction at ILL have been added. These handle the basic reduction in SumInLambda mode. Included algorithms: +* Added algorithm ``PolarizationEfficiencyCor`` which calls ``PolarizationCorrectionFredrikze`` or ``PolarizationCorrectionWildes`` depending on chosen ``Method`` property. +* Added algorithms that help create a matrix workspace with polarization efficiencies ready to be used with ``PolarizationEfficiencyCor`` + + - ``CreatePolarizationEfficiencies`` creates efficiencies from polynomial coefficients + - ``JoinISISPolarizationEfficiencies`` joins individual efficiencies into one matrix workspace + - ``LoadISISPolarizationEfficiencies`` loads efficiencies form files +* Algorithms for reflectometry reduction at ILL have been added. These handle the basic reduction in SumInLambda mode. Included algorithms: - :ref:`algm-ReflectometryILLPreprocess` - :ref:`algm-ReflectometryILLSumForeground` - :ref:`algm-ReflectometryILLPolarizationCor` - :ref:`algm-ReflectometryILLConvertToQ` -- A new algorithm :ref:`algm-ReflectometryMomentumTransfer` provides conversion to momentum transfer and :math:`Q_{z}` resolution calculation for relfectivity workspaces. +* A new algorithm :ref:`algm-ReflectometryMomentumTransfer` provides conversion to momentum transfer and :math:`Q_{z}` resolution calculation for relfectivity workspaces. Improvements ############ diff --git a/qt/scientific_interfaces/ISISReflectometry/ExperimentOptionDefaults.cpp b/qt/scientific_interfaces/ISISReflectometry/ExperimentOptionDefaults.cpp index 584e9374506005b6990cbe31a216ceead5bf28d3..1f2547d15594ab9cf31d57dc3bf4ddd54c164058 100644 --- a/qt/scientific_interfaces/ISISReflectometry/ExperimentOptionDefaults.cpp +++ b/qt/scientific_interfaces/ISISReflectometry/ExperimentOptionDefaults.cpp @@ -25,8 +25,8 @@ std::ostream &operator<<(std::ostream &os, ExperimentOptionDefaults const &defaults) { os << "ExperimentOptionDefaults: { AnalysisMode: '" << defaults.AnalysisMode << ", \nPolarizationAnalysis: '" << defaults.PolarizationAnalysis - << "',\nCRho: '" << defaults.CRho << "',\nCAlpha: '" << defaults.CAlpha - << "',\nCAp: '" << defaults.CAp << "', \nCPp: '" << defaults.CPp + << "',\nRho: '" << defaults.CRho << "',\nAlpha: '" << defaults.CAlpha + << "',\nAp: '" << defaults.CAp << "', \nPp: '" << defaults.CPp << "',\nSummationType: '" << defaults.SummationType << "', \nReductionType: '" << defaults.ReductionType; if (defaults.TransRunStartOverlap) diff --git a/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp b/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp index e318d9eb0f150c46862b03ce0ea25a40b762a846..8ccad3eb878ce849cd0ae2eb26385a414dbc9c2b 100644 --- a/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp +++ b/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp @@ -148,10 +148,10 @@ void QtReflSettingsView::registerExperimentSettingsWidgets( registerSettingWidget(*m_ui.startOverlapEdit, "StartOverlap", alg); registerSettingWidget(*m_ui.endOverlapEdit, "EndOverlap", alg); registerSettingWidget(*m_ui.polCorrComboBox, "PolarizationAnalysis", alg); - registerSettingWidget(*m_ui.CRhoEdit, "CRho", alg); - registerSettingWidget(*m_ui.CAlphaEdit, "CAlpha", alg); - registerSettingWidget(*m_ui.CApEdit, "CAp", alg); - registerSettingWidget(*m_ui.CPpEdit, "CPp", alg); + registerSettingWidget(*m_ui.CRhoEdit, "Rho", alg); + registerSettingWidget(*m_ui.CAlphaEdit, "Alpha", alg); + registerSettingWidget(*m_ui.CApEdit, "Ap", alg); + registerSettingWidget(*m_ui.CPpEdit, "Pp", alg); registerSettingWidget(stitchOptionsLineEdit(), "Params", alg); } diff --git a/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp b/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp index bf42ae1a721e00361965d8d25acc6cc92ad6216c..ba36bd8ceaf261a29c0a1fddb92d4125a6c9e64b 100644 --- a/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp +++ b/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp @@ -213,10 +213,10 @@ OptionsQMap ReflSettingsPresenter::getReductionOptions() const { if (m_view->experimentSettingsEnabled()) { addIfNotEmpty(options, "AnalysisMode", m_view->getAnalysisMode()); - addIfNotEmpty(options, "CRho", m_view->getCRho()); - addIfNotEmpty(options, "CAlpha", m_view->getCAlpha()); - addIfNotEmpty(options, "CAp", m_view->getCAp()); - addIfNotEmpty(options, "CPp", m_view->getCPp()); + addIfNotEmpty(options, "Rho", m_view->getCRho()); + addIfNotEmpty(options, "Alpha", m_view->getCAlpha()); + addIfNotEmpty(options, "Ap", m_view->getCAp()); + addIfNotEmpty(options, "Pp", m_view->getCPp()); addIfNotEmpty(options, "PolarizationAnalysis", m_view->getPolarisationCorrections()); addIfNotEmpty(options, "StartOverlap", m_view->getStartOverlap()); diff --git a/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h b/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h index 493ce7187a897fdf0bebee911c692f4ceb5eb7c3..2a62575dc4370038139fb528a895526dca021a90 100644 --- a/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h +++ b/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h @@ -204,10 +204,10 @@ public: auto options = presenter.getReductionOptions(); TS_ASSERT_EQUALS(variantToString(options["PolarizationAnalysis"]), "PNR"); - TS_ASSERT_EQUALS(variantToString(options["CRho"]), "2.5,0.4,1.1"); - TS_ASSERT_EQUALS(variantToString(options["CAlpha"]), "0.6,0.9,1.2"); - TS_ASSERT_EQUALS(variantToString(options["CAp"]), "100.0,17.0,44.0"); - TS_ASSERT_EQUALS(variantToString(options["CPp"]), "0.54,0.33,1.81"); + TS_ASSERT_EQUALS(variantToString(options["Rho"]), "2.5,0.4,1.1"); + TS_ASSERT_EQUALS(variantToString(options["Alpha"]), "0.6,0.9,1.2"); + TS_ASSERT_EQUALS(variantToString(options["Ap"]), "100.0,17.0,44.0"); + TS_ASSERT_EQUALS(variantToString(options["Pp"]), "0.54,0.33,1.81"); TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); }