Skip to content
Snippets Groups Projects
ReflectometryReductionOneAuto2.cpp 33.3 KiB
Newer Older
#include "MantidAlgorithms/ReflectometryReductionOneAuto2.h"
#include "MantidAPI/MatrixWorkspace.h"
Raquel Alvarez's avatar
Raquel Alvarez committed
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidAlgorithms/BoostOptionalToAlgorithmProperty.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/CompositeValidator.h"
#include "MantidKernel/EnabledWhenProperty.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/MandatoryValidator.h"
#include "MantidKernel/make_unique.h"
namespace Mantid {
namespace Algorithms {

using namespace Mantid::API;
using namespace Mantid::Geometry;
using namespace Mantid::Kernel;

// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(ReflectometryReductionOneAuto2)

//----------------------------------------------------------------------------------------------

/// Algorithm's name for identification. @see Algorithm::name
const std::string ReflectometryReductionOneAuto2::name() const {
  return "ReflectometryReductionOneAuto";
}

/// Algorithm's version for identification. @see Algorithm::version
int ReflectometryReductionOneAuto2::version() const { return 2; }

/// Algorithm's category for identification. @see Algorithm::category
const std::string ReflectometryReductionOneAuto2::category() const {
  return "Reflectometry\\ISIS";
}

/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string ReflectometryReductionOneAuto2::summary() const {
  return "Reduces a single TOF/Lambda reflectometry run into a mod Q vs I/I0 "
         "workspace attempting to pick instrument parameters for missing "
         "properties";
}

/** Validate transmission runs
 *
 * @return :: result of the validation as a map
 */
std::map<std::string, std::string>
ReflectometryReductionOneAuto2::validateInputs() {

  std::map<std::string, std::string> results;

  // Validate transmission runs only if our input workspace is a group
  if (!checkGroups())
    return results;

  auto group = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
      getPropertyValue("InputWorkspace"));
  if (!group)
    return results;

  // First transmission run
  const std::string firstStr = getPropertyValue("FirstTransmissionRun");
  if (!firstStr.empty()) {
    auto firstTransGroup =
        AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(firstStr);
    // If it is not a group, we don't need to validate its size
    if (!firstTransGroup)
      return results;

    const bool polarizationCorrections =
        getPropertyValue("PolarizationAnalysis") != "None";

    if (group->size() != firstTransGroup->size() && !polarizationCorrections) {
      // If they are not the same size then we cannot associate a transmission
      // group member with every input group member.
      results["FirstTransmissionRun"] =
          "FirstTransmissionRun group must be the "
          "same size as the InputWorkspace group "
          "when polarization analysis is 'None'.";
    }
  }

  // The same for the second transmission run
  const std::string secondStr = getPropertyValue("SecondTransmissionRun");
  if (!secondStr.empty()) {
    auto secondTransGroup =
        AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(secondStr);
    // If it is not a group, we don't need to validate its size
    if (!secondTransGroup)
      return results;
    const bool polarizationCorrections =
        getPropertyValue("PolarizationAnalysis") != "None";

    if (group->size() != secondTransGroup->size() && !polarizationCorrections) {
      results["SecondTransmissionRun"] =
          "SecondTransmissionRun group must be the "
          "same size as the InputWorkspace group "
          "when polarization analysis is 'None'.";
    }
  }

  return results;
}

/** Initialize the algorithm's properties.
void ReflectometryReductionOneAuto2::init() {

  // Input ws
  declareProperty(
      make_unique<WorkspaceProperty<MatrixWorkspace>>(
          "InputWorkspace", "", Direction::Input, PropertyMode::Mandatory),
      "Input run in TOF or wavelength");

  // Reduction type
  initReductionProperties();
  const std::vector<std::string> analysisMode{"PointDetectorAnalysis",
                                              "MultiDetectorAnalysis"};
  auto analysisModeValidator =
      boost::make_shared<StringListValidator>(analysisMode);
  declareProperty("AnalysisMode", analysisMode[0], analysisModeValidator,
                  "Analysis mode. This property is only used when "
                  "ProcessingInstructions is not set.",
                  Direction::Input);

  // Processing instructions
  declareProperty(make_unique<PropertyWithValue<std::string>>(
                      "ProcessingInstructions", "", Direction::Input),
                  "Grouping pattern of workspace indices to yield only the"
                  " detectors of interest. See GroupDetectors for syntax.");

  // Theta
  declareProperty("ThetaIn", Mantid::EMPTY_DBL(), "Angle in degrees",
                  Direction::Input);

  // ThetaLogName
  declareProperty("ThetaLogName", "",
                  "The name ThetaIn can be found in the run log as");

  // Whether to correct detectors
  declareProperty(
      make_unique<PropertyWithValue<bool>>("CorrectDetectors", true,
                                           Direction::Input),
      "Moves detectors to twoTheta if ThetaIn or ThetaLogName is given");
  // Detector position correction type
Gemma Guest's avatar
Gemma Guest committed
  const std::vector<std::string> correctionType{"VerticalShift",
                                                "RotateAroundSample"};
  auto correctionTypeValidator = boost::make_shared<CompositeValidator>();
  correctionTypeValidator->add(
      boost::make_shared<MandatoryValidator<std::string>>());
  correctionTypeValidator->add(
      boost::make_shared<StringListValidator>(correctionType));
  declareProperty(
      "DetectorCorrectionType", correctionType[0], correctionTypeValidator,
      "When correcting detector positions, this determines whether detectors"
      "should be shifted vertically or rotated around the sample position.",
  setPropertySettings("DetectorCorrectionType",
                      make_unique<Kernel::EnabledWhenProperty>(
                          "CorrectDetectors", IS_EQUAL_TO, "1"));
  // Wavelength limits
  declareProperty("WavelengthMin", Mantid::EMPTY_DBL(),
                  "Wavelength Min in angstroms", Direction::Input);
  declareProperty("WavelengthMax", Mantid::EMPTY_DBL(),
                  "Wavelength Max in angstroms", Direction::Input);

  // Monitor properties
  initMonitorProperties();
  // Normalization by integrated monitors
  declareProperty("NormalizeByIntegratedMonitors", true,
                  "Normalize by dividing by the integrated monitors.");

  // Init properties for transmission normalization
  initTransmissionProperties();

  // Init properties for algorithmic corrections
  initAlgorithmicProperties(true);

  // Momentum transfer properties
  initMomentumTransferProperties();

  // Polarization correction
  std::vector<std::string> propOptions = {"None", "PA", "PNR", "ParameterFile"};
  declareProperty("PolarizationAnalysis", "None",
                  boost::make_shared<StringListValidator>(propOptions),
                  "Polarization analysis mode.");
  declareProperty(
      Kernel::make_unique<ArrayProperty<double>>("Pp", Direction::Input),
      "Effective polarizing power of the polarizing system. "
      "Expressed as a ratio 0 &lt; Pp &lt; 1");
      Kernel::make_unique<ArrayProperty<double>>("Ap", Direction::Input),
      "Effective polarizing power of the analyzing system. "
      "Expressed as a ratio 0 &lt; Ap &lt; 1");
      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.");
  setPropertyGroup("PolarizationAnalysis", "Polarization Corrections");
  setPropertyGroup("Pp", "Polarization Corrections");
  setPropertyGroup("Ap", "Polarization Corrections");
  setPropertyGroup("Rho", "Polarization Corrections");
  setPropertyGroup("Alpha", "Polarization Corrections");
  // Init properties for diagnostics
  initDebugProperties();
Gemma Guest's avatar
Gemma Guest committed

  // Output workspace in Q
  declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
                      "OutputWorkspaceBinned", "", Direction::Output),
                  "Output workspace in Q (rebinned workspace)");

  // Output workspace in Q (unbinned)
  declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
                      "OutputWorkspace", "", Direction::Output),
                  "Output workspace in Q (native binning)");

  // Output workspace in wavelength
  declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
                      "OutputWorkspaceWavelength", "", Direction::Output),
                  "Output workspace in wavelength");
}

/** Execute the algorithm.
void ReflectometryReductionOneAuto2::exec() {

  MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
  auto instrument = inputWS->getInstrument();

  IAlgorithm_sptr alg = createChildAlgorithm("ReflectometryReductionOne");
  alg->initialize();
  // Mandatory properties
  alg->setProperty("SummationType", getPropertyValue("SummationType"));
  alg->setProperty("ReductionType", getPropertyValue("ReductionType"));
  alg->setProperty("IncludePartialBins",
                   getPropertyValue("IncludePartialBins"));
  alg->setProperty("Diagnostics", getPropertyValue("Diagnostics"));
  double wavMin = checkForMandatoryInstrumentDefault<double>(
      this, "WavelengthMin", instrument, "LambdaMin");
  alg->setProperty("WavelengthMin", wavMin);
  double wavMax = checkForMandatoryInstrumentDefault<double>(
      this, "WavelengthMax", instrument, "LambdaMax");
  alg->setProperty("WavelengthMax", wavMax);

  const auto instructions =
      populateProcessingInstructions(alg, instrument, inputWS);

  // Now that we know the detectors of interest, we can move them if necessary
  // (i.e. if theta is given). If not, we calculate theta from the current
  // detector positions
  bool correctDetectors = getProperty("CorrectDetectors");
  double theta;
  if (!getPointerToProperty("ThetaIn")->isDefault()) {
    theta = getProperty("ThetaIn");
  } else if (!getPropertyValue("ThetaLogName").empty()) {
    theta = getThetaFromLogs(inputWS, getPropertyValue("ThetaLogName"));
    // Calculate theta from detector positions
    theta = calculateTheta(instructions, inputWS);
    // Never correct detector positions if ThetaIn or ThetaLogName is not
    // specified
    correctDetectors = false;

  // Pass theta to the child algorithm
  alg->setProperty("ThetaIn", theta);
    inputWS = correctDetectorPositions(instructions, inputWS, 2 * theta);
  // Optional properties

  populateMonitorProperties(alg, instrument);
  alg->setPropertyValue("NormalizeByIntegratedMonitors",
                        getPropertyValue("NormalizeByIntegratedMonitors"));
  bool transRunsFound = populateTransmissionProperties(alg);
  if (transRunsFound)
    alg->setProperty("StrictSpectrumChecking",
                     getPropertyValue("StrictSpectrumChecking"));
  else
    populateAlgorithmicCorrectionProperties(alg, instrument);

  alg->setProperty("InputWorkspace", inputWS);
  alg->execute();

  MatrixWorkspace_sptr IvsLam = alg->getProperty("OutputWorkspaceWavelength");
  MatrixWorkspace_sptr IvsQ = alg->getProperty("OutputWorkspace");

  std::vector<double> params;
  MatrixWorkspace_sptr IvsQB = rebinAndScale(IvsQ, theta, params);

  setProperty("OutputWorkspaceWavelength", IvsLam);
  setProperty("OutputWorkspace", IvsQ);
  setProperty("OutputWorkspaceBinned", IvsQB);

  // Set other properties so they can be updated in the Reflectometry interface
  setProperty("ThetaIn", theta);
  if (!params.empty()) {
    if (params.size() == 3) {
      setProperty("MomentumTransferMin", params[0]);
      setProperty("MomentumTransferStep", -params[1]);
      setProperty("MomentumTransferMax", params[2]);
    } else {
      setProperty("MomentumTransferMin", IvsQ->x(0).front());
      setProperty("MomentumTransferMax", IvsQ->x(0).back());
      setProperty("MomentumTransferStep", -params[0]);
  if (getPointerToProperty("ScaleFactor")->isDefault())
    setProperty("ScaleFactor", 1.0);
/** 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
 */
std::vector<std::string> ReflectometryReductionOneAuto2::getDetectorNames(
    const std::string &instructions, MatrixWorkspace_sptr inputWS) {

  std::vector<std::string> wsIndices;
  boost::split(wsIndices, instructions, boost::is_any_of(":,-+"));
  // vector of comopnents
  std::vector<std::string> detectors;
    for (const auto &wsIndex : wsIndices) {
      size_t index = boost::lexical_cast<size_t>(wsIndex);
      auto detector = inputWS->getDetector(index);
      auto parent = detector->getParent();
      if (parent) {
        auto parentType = parent->type();
        auto detectorName = (parentType == "Instrument") ? detector->getName()
Gemma Guest's avatar
Gemma Guest committed
                                                         : parent->getName();
        detectors.push_back(detectorName);
      }
  } catch (boost::bad_lexical_cast &) {
Gemma Guest's avatar
Gemma Guest committed
    throw std::runtime_error("Invalid processing instructions: " +
                             instructions);
/** 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
 */
MatrixWorkspace_sptr ReflectometryReductionOneAuto2::correctDetectorPositions(
Gemma Guest's avatar
Gemma Guest committed
    const std::string &instructions, MatrixWorkspace_sptr inputWS,
    const double twoTheta) {

  auto detectorsOfInterest = getDetectorNames(instructions, inputWS);

  // Detectors of interest may be empty. This happens for instance when we input
  // a workspace that was previously reduced using this algorithm. In this case,
  // we shouldn't correct the detector positions
  if (detectorsOfInterest.empty())
    return inputWS;

  const std::set<std::string> detectorSet(detectorsOfInterest.begin(),
                                          detectorsOfInterest.end());
  const std::string correctionType = getProperty("DetectorCorrectionType");

  MatrixWorkspace_sptr corrected = inputWS;

  for (const auto &detector : detectorSet) {
    IAlgorithm_sptr alg =
        createChildAlgorithm("SpecularReflectionPositionCorrect");
    alg->setProperty("InputWorkspace", corrected);
    alg->setProperty("TwoTheta", twoTheta);
    alg->setProperty("DetectorCorrectionType", correctionType);
    alg->setProperty("DetectorComponentName", detector);
    alg->execute();
    corrected = alg->getProperty("OutputWorkspace");
  }

  return corrected;
}

/** 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)
 */
double
ReflectometryReductionOneAuto2::calculateTheta(const std::string &instructions,
                                               MatrixWorkspace_sptr inputWS) {
  const auto detectorsOfInterest = getDetectorNames(instructions, inputWS);

  // Detectors of interest may be empty. This happens for instance when we input
  // a workspace that was previously reduced using this algorithm. In this case,
  // we can't calculate theta
  if (detectorsOfInterest.empty())
    return 0.0;

  IAlgorithm_sptr alg =
      createChildAlgorithm("SpecularReflectionCalculateTheta");
  alg->setProperty("InputWorkspace", inputWS);
  alg->setProperty("DetectorComponentName", detectorsOfInterest[0]);
  alg->execute();
  const double theta = alg->getProperty("TwoTheta");
  // Take a factor of 0.5 of the detector position, which is expected to be at
  // 2 * theta
  return theta * 0.5;
/** Set algorithmic correction properties
 *
 * @param alg :: ReflectometryReductionOne algorithm
 * @param instrument :: The instrument attached to the workspace
 */
void ReflectometryReductionOneAuto2::populateAlgorithmicCorrectionProperties(
    IAlgorithm_sptr alg, Instrument_const_sptr instrument) {

  // With algorithmic corrections, monitors should not be integrated, see below
  const std::string correctionAlgorithm = getProperty("CorrectionAlgorithm");

  if (correctionAlgorithm == "PolynomialCorrection") {
    alg->setProperty("NormalizeByIntegratedMonitors", false);
    alg->setProperty("CorrectionAlgorithm", "PolynomialCorrection");
    alg->setPropertyValue("Polynomial", getPropertyValue("Polynomial"));

  } else if (correctionAlgorithm == "ExponentialCorrection") {
    alg->setProperty("NormalizeByIntegratedMonitors", false);
    alg->setProperty("CorrectionAlgorithm", "ExponentialCorrection");
    alg->setProperty("C0", getPropertyValue("C0"));
    alg->setProperty("C1", getPropertyValue("C1"));

  } else if (correctionAlgorithm == "AutoDetect") {
    // Figure out what to do from the instrument
    try {
      const auto corrVec = instrument->getStringParameter("correction");
      if (corrVec.empty()) {
        throw std::runtime_error(
            "Could not find parameter 'correction' in "
            "parameter file. Cannot auto detect the type of "
            "correction.");
      }

      const std::string correctionStr = corrVec[0];

      if (correctionStr == "polynomial") {
        const auto polyVec = instrument->getStringParameter("polystring");
        if (polyVec.empty())
          throw std::runtime_error("Could not find parameter 'polystring' in "
                                   "parameter file. Cannot apply polynomial "
                                   "correction.");
        alg->setProperty("CorrectionAlgorithm", "PolynomialCorrection");
        alg->setProperty("Polynomial", polyVec[0]);
      } else if (correctionStr == "exponential") {
        const auto c0Vec = instrument->getStringParameter("C0");
        if (c0Vec.empty())
          throw std::runtime_error(
              "Could not find parameter 'C0' in parameter "
              "file. Cannot apply exponential correction.");
        const auto c1Vec = instrument->getStringParameter("C1");
        if (c1Vec.empty())
          throw std::runtime_error(
              "Could not find parameter 'C1' in parameter "
              "file. Cannot apply exponential correction.");
        alg->setProperty("C0", c0Vec[0]);
        alg->setProperty("C1", c1Vec[0]);
      }
      alg->setProperty("NormalizeByIntegratedMonitors", false);
    } catch (std::runtime_error &e) {
      g_log.error() << e.what()
                    << ". Polynomial correction will not be performed.";
      alg->setProperty("CorrectionAlgorithm", "None");
    }
  } else {
    alg->setProperty("CorrectionAlgorithm", "None");
  }
}

/** 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
 */
MatrixWorkspace_sptr
ReflectometryReductionOneAuto2::rebinAndScale(MatrixWorkspace_sptr inputWS,
                                              const double theta,
                                              std::vector<double> &params) {

  Property *qStepProp = getProperty("MomentumTransferStep");
  double qstep;
  if (!qStepProp->isDefault()) {
    qstep = getProperty("MomentumTransferStep");
    qstep = -qstep;
  } else {
    if (theta == 0.0) {
      throw std::runtime_error(
          "Theta determined from the detector positions is "
          "0.0. Please provide a value for theta manually "
          "or correct the detector position before running "
          "this algorithm.");
    }

    IAlgorithm_sptr calcRes = createChildAlgorithm("NRCalculateSlitResolution");
    calcRes->setProperty("Workspace", inputWS);
Gemma Guest's avatar
Gemma Guest committed
    calcRes->setProperty("TwoTheta", 2 * theta);
    calcRes->execute();

    if (!calcRes->isExecuted()) {
Gemma Guest's avatar
Gemma Guest committed
      g_log.error(
          "NRCalculateSlitResolution failed. Workspace in Q will not be "
          "rebinned. Please provide dQ/Q.");
      return inputWS;
    }
    qstep = calcRes->getProperty("Resolution");
    qstep = -qstep;
  }

  Property *qMin = getProperty("MomentumTransferMin");
  Property *qMax = getProperty("MomentumTransferMax");
  if (!qMin->isDefault() && !qMax->isDefault()) {
    double qmin = getProperty("MomentumTransferMin");
    double qmax = getProperty("MomentumTransferMax");
    params.push_back(qmin);
    params.push_back(qstep);
    params.push_back(qmax);
  } else {
    params.push_back(qstep);
  }

  // Rebin
  IAlgorithm_sptr algRebin = createChildAlgorithm("Rebin");
  algRebin->initialize();
  algRebin->setProperty("InputWorkspace", inputWS);
  algRebin->setProperty("OutputWorkspace", inputWS);
  algRebin->setProperty("Params", params);
  algRebin->execute();
  MatrixWorkspace_sptr IvsQ = algRebin->getProperty("OutputWorkspace");

  // Scale (optional)
  Property *scaleProp = getProperty("ScaleFactor");
  if (!scaleProp->isDefault()) {
    double scaleFactor = getProperty("ScaleFactor");
    IAlgorithm_sptr algScale = createChildAlgorithm("Scale");
    algScale->initialize();
    algScale->setProperty("InputWorkspace", IvsQ);
    algScale->setProperty("OutputWorkspace", IvsQ);
    algScale->setProperty("Factor", 1.0 / scaleFactor);
    algScale->execute();
    IvsQ = algScale->getProperty("OutputWorkspace");
  }

  return IvsQ;
}

/** Check if input workspace is a group
bool ReflectometryReductionOneAuto2::checkGroups() {

  const std::string wsName = getPropertyValue("InputWorkspace");

  try {
    auto ws =
        AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(wsName);
    if (ws)
      return true;
  } catch (...) {
  }
  return false;
}

/** Process groups. Groups are processed differently depending on transmission
 * runs and polarization analysis. If transmission run is a matrix workspace, it
 * will be applied to each of the members in the input workspace group. If
 * transmission run is a workspace group, the behaviour is different depending
 * on polarization analysis. If polarization analysis is off (i.e.
 * 'PolarizationAnalysis' is set to 'None') each item in the transmission group
 * is associated with the corresponding item in the input workspace group. If
 * polarization analysis is on (i.e. 'PolarizationAnalysis' is 'PA' or 'PNR')
 * 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;

  // Get our input workspace group
  auto group = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
      getPropertyValue("InputWorkspace"));
  // Get name of IvsQ workspace (native binning)
  const std::string outputIvsQ = getPropertyValue("OutputWorkspace");
  // Get name of IvsQ (native binning) workspace
  const std::string outputIvsQBinned =
      getPropertyValue("OutputWorkspaceBinned");
  // Get name of IvsLam workspace
  const std::string outputIvsLam =
      getPropertyValue("OutputWorkspaceWavelength");

  // Create a copy of ourselves
  Algorithm_sptr alg =
      createChildAlgorithm(name(), -1, -1, isLogging(), version());
  alg->setChild(false);
  alg->setRethrows(true);

  // Copy all the non-workspace properties over
  const std::vector<Property *> props = getProperties();
  for (auto &prop : props) {
    if (prop) {
      IWorkspaceProperty *wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
      if (!wsProp)
        alg->setPropertyValue(prop->name(), prop->value());
    }
  }

  const bool polarizationAnalysisOn =
      getPropertyValue("PolarizationAnalysis") != "None";

  // Check if the transmission runs are groups or not
  const std::string firstTrans = getPropertyValue("FirstTransmissionRun");
  WorkspaceGroup_sptr firstTransG;
  MatrixWorkspace_sptr firstTransSum;
  if (!firstTrans.empty()) {
    auto firstTransWS =
        AnalysisDataService::Instance().retrieveWS<Workspace>(firstTrans);
    firstTransG = boost::dynamic_pointer_cast<WorkspaceGroup>(firstTransWS);
    if (!firstTransG) {
      alg->setProperty("FirstTransmissionRun", firstTrans);
    } else if (polarizationAnalysisOn) {
      firstTransSum = sumTransmissionWorkspaces(firstTransG);
    }
  }
  const std::string secondTrans = getPropertyValue("SecondTransmissionRun");
  WorkspaceGroup_sptr secondTransG;
  MatrixWorkspace_sptr secondTransSum;
  if (!secondTrans.empty()) {
    auto secondTransWS =
        AnalysisDataService::Instance().retrieveWS<Workspace>(secondTrans);
    secondTransG = boost::dynamic_pointer_cast<WorkspaceGroup>(secondTransWS);
      alg->setProperty("SecondTransmissionRun", secondTrans);
    } else if (polarizationAnalysisOn) {
      secondTransSum = sumTransmissionWorkspaces(secondTransG);
  std::vector<std::string> IvsQGroup, IvsQUnbinnedGroup, IvsLamGroup;
  // Execute algorithm over each group member
  for (size_t i = 0; i < group->size(); ++i) {

    const std::string IvsQName = outputIvsQ + "_" + std::to_string(i + 1);
    const std::string IvsQBinnedName =
        outputIvsQBinned + "_" + std::to_string(i + 1);
    const std::string IvsLamName = outputIvsLam + "_" + std::to_string(i + 1);

    if (firstTransG) {
      if (!polarizationAnalysisOn)
        alg->setProperty("FirstTransmissionRun",
Raquel Alvarez's avatar
Raquel Alvarez committed
                         firstTransG->getItem(i)->getName());
      else
        alg->setProperty("FirstTransmissionRun", firstTransSum);
    if (secondTransG) {
      if (!polarizationAnalysisOn)
        alg->setProperty("SecondTransmissionRun",
Raquel Alvarez's avatar
Raquel Alvarez committed
                         secondTransG->getItem(i)->getName());
      else
        alg->setProperty("SecondTransmissionRun", secondTransSum);
Raquel Alvarez's avatar
Raquel Alvarez committed
    alg->setProperty("InputWorkspace", group->getItem(i)->getName());
    alg->setProperty("OutputWorkspace", IvsQName);
    alg->setProperty("OutputWorkspaceBinned", IvsQBinnedName);
    alg->setProperty("OutputWorkspaceWavelength", IvsLamName);
    alg->execute();

    IvsQGroup.push_back(IvsQName);
    IvsQUnbinnedGroup.push_back(IvsQBinnedName);
    IvsLamGroup.push_back(IvsLamName);
  }

  // Group the IvsQ and IvsLam workspaces
  Algorithm_sptr groupAlg = createChildAlgorithm("GroupWorkspaces");
  groupAlg->setChild(false);
  groupAlg->setRethrows(true);
  groupAlg->setProperty("InputWorkspaces", IvsLamGroup);
  groupAlg->setProperty("OutputWorkspace", outputIvsLam);
  groupAlg->execute();
  groupAlg->setProperty("InputWorkspaces", IvsQGroup);
  groupAlg->setProperty("OutputWorkspace", outputIvsQ);
  groupAlg->execute();
  groupAlg->setProperty("InputWorkspaces", IvsQUnbinnedGroup);
  groupAlg->setProperty("OutputWorkspace", outputIvsQBinned);
  groupAlg->execute();
  // Set other properties so they can be updated in the Reflectometry interface
  setPropertyValue("ThetaIn", alg->getPropertyValue("ThetaIn"));
  setPropertyValue("MomentumTransferMin",
                   alg->getPropertyValue("MomentumTransferMin"));
  setPropertyValue("MomentumTransferMax",
                   alg->getPropertyValue("MomentumTransferMax"));
  setPropertyValue("MomentumTransferStep",
                   alg->getPropertyValue("MomentumTransferStep"));
  setPropertyValue("ScaleFactor", alg->getPropertyValue("ScaleFactor"));

  if (!polarizationAnalysisOn) {
    // No polarization analysis. Reduction stops here
    setPropertyValue("OutputWorkspace", outputIvsQ);
    setPropertyValue("OutputWorkspaceBinned", outputIvsQBinned);
    setPropertyValue("OutputWorkspaceWavelength", outputIvsLam);
    return true;
  }

  applyPolarizationCorrection(outputIvsLam);

  // Now we've overwritten the IvsLam workspaces, we'll need to recalculate
  // the IvsQ ones
  alg->setProperty("FirstTransmissionRun", "");
  alg->setProperty("SecondTransmissionRun", "");
  alg->setProperty("CorrectionAlgorithm", "None");
  alg->setProperty("ProcessingInstructions", "0");
  for (size_t i = 0; i < group->size(); ++i) {
    const std::string IvsQName = outputIvsQ + "_" + std::to_string(i + 1);
    const std::string IvsQBinnedName =
        outputIvsQBinned + "_" + std::to_string(i + 1);
    const std::string IvsLamName = outputIvsLam + "_" + std::to_string(i + 1);
    alg->setProperty("InputWorkspace", IvsLamName);
    alg->setProperty("OutputWorkspace", IvsQName);
Raquel Alvarez's avatar
Raquel Alvarez committed
    alg->setProperty("OutputWorkspaceBinned", IvsQBinnedName);
    alg->setProperty("OutputWorkspaceWavelength", IvsLamName);
    alg->execute();
  }

  setPropertyValue("OutputWorkspace", outputIvsQ);
  setPropertyValue("OutputWorkspaceBinned", outputIvsQBinned);
  setPropertyValue("OutputWorkspaceWavelength", outputIvsLam);
/** Construct a polarization efficiencies workspace based on values of input
 * properties.
 */
std::tuple<API::MatrixWorkspace_sptr, std::string, std::string>
ReflectometryReductionOneAuto2::getPolarizationEfficiencies() {
  auto groupIvsLam = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
      getPropertyValue("OutputWorkspaceWavelength"));

  std::string const paMethod = getPropertyValue("PolarizationAnalysis");
  Workspace_sptr workspace = groupIvsLam->getItem(0);
  MatrixWorkspace_sptr efficiencies;
  std::string correctionMethod;
  std::string correctionOption;

  if (paMethod == "ParameterFile") {
    auto effAlg = createChildAlgorithm("ExtractPolarizationEfficiencies");
    effAlg->setProperty("InputWorkspace", workspace);
    effAlg->execute();
    efficiencies = effAlg->getProperty("OutputWorkspace");
    correctionMethod = effAlg->getPropertyValue("CorrectionMethod");
    correctionOption = effAlg->getPropertyValue("CorrectionOption");
  } else {
    auto effAlg = createChildAlgorithm("CreatePolarizationEfficiencies");
    effAlg->setProperty("InputWorkspace", workspace);
    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();
    efficiencies = effAlg->getProperty("OutputWorkspace");
    correctionMethod = "Fredrikze";
    correctionOption = paMethod;
  }
  return std::make_tuple(efficiencies, correctionMethod, correctionOption);
}

/**
 * Apply a polarization correction to workspaces in lambda.
 * @param outputIvsLam :: Name of a workspace group to apply the correction to.
 */
void ReflectometryReductionOneAuto2::applyPolarizationCorrection(
    std::string const &outputIvsLam) {
  MatrixWorkspace_sptr efficiencies;
  std::string correctionMethod;
  std::string correctionOption;
  std::tie(efficiencies, correctionMethod, correctionOption) =
      getPolarizationEfficiencies();

  Algorithm_sptr polAlg = createChildAlgorithm("PolarizationEfficiencyCor");
  polAlg->setChild(false);
  polAlg->setRethrows(true);
  polAlg->setProperty("OutputWorkspace", outputIvsLam);
  polAlg->setProperty("Efficiencies", efficiencies);
  polAlg->setProperty("CorrectionMethod", correctionMethod);

  if (correctionMethod == "Fredrikze") {
    polAlg->setProperty("InputWorkspaceGroup", outputIvsLam);
    polAlg->setProperty("PolarizationAnalysis", correctionOption);
  } else {
    throw std::invalid_argument("Unsupported polarization correction method: " +
                                correctionMethod);
 * 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) {

  const std::string transSum = "trans_sum";
  Workspace_sptr sumWS = transGroup->getItem(0)->clone();

  /// For this step to appear in the history of the output workspaces I need to
  /// set child to false and work with the ADS
  auto plusAlg = createChildAlgorithm("Plus");
  plusAlg->setChild(false);
  plusAlg->initialize();

  for (size_t item = 1; item < transGroup->size(); item++) {
    plusAlg->setProperty("LHSWorkspace", sumWS);
    plusAlg->setProperty("RHSWorkspace", transGroup->getItem(item));
    plusAlg->setProperty("OutputWorkspace", transSum);
    plusAlg->execute();
    sumWS = AnalysisDataService::Instance().retrieve(transSum);
  }
  MatrixWorkspace_sptr result =
      boost::dynamic_pointer_cast<MatrixWorkspace>(sumWS);
  AnalysisDataService::Instance().remove(transSum);
  return result;
}

} // namespace Algorithms
} // namespace Mantid