Skip to content
Snippets Groups Projects
SetupEQSANSReduction.cpp 51.6 KiB
Newer Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidWorkflowAlgorithms/SetupEQSANSReduction.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/RebinParamsValidator.h"
#include "MantidKernel/EnabledWhenProperty.h"
#include "MantidKernel/VisibleWhenProperty.h"
#include "MantidAPI/FileProperty.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidAPI/AlgorithmProperty.h"
#include "MantidKernel/PropertyManagerDataService.h"
#include "MantidKernel/PropertyManager.h"
#include "Poco/NumberFormatter.h"
namespace Mantid {
namespace WorkflowAlgorithms {

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

using namespace Kernel;
using namespace API;
using namespace Geometry;

void SetupEQSANSReduction::init() {
  // Load options
  std::string load_grp = "Load Options";
  declareProperty("UseConfigTOFCuts", false,
                  "If true, the edges of the TOF distribution will be cut "
                  "according to the configuration file");
  declareProperty("LowTOFCut", 0.0, "TOF value below which events will not be "
                                    "loaded into the workspace at load-time");
  declareProperty("HighTOFCut", 0.0, "TOF value above which events will not be "
                                     "loaded into the workspace at load-time");
  declareProperty("WavelengthStep", 0.1, "Wavelength steps to be used when "
                                         "rebinning the data before performing "
                                         "the reduction");
  declareProperty("UseConfigMask", false, "If true, the masking information "
                                          "found in the configuration file "
                                          "will be used");
  declareProperty("UseConfig", true,
                  "If true, the best configuration file found will be used");
  declareProperty("CorrectForFlightPath", false,
                  "If true, the TOF will be modified for the true flight path "
                  "from the sample to the detector pixel");

  declareProperty("SkipTOFCorrection", false,
                  "If true, the EQSANS TOF correction will be skipped");
  declareProperty("PreserveEvents", false,
                  "If true, the output workspace will be an event workspace");

  declareProperty(
      "SampleDetectorDistance", EMPTY_DBL(),
      "Sample to detector distance to use (overrides meta data), in mm");
  declareProperty("SampleDetectorDistanceOffset", EMPTY_DBL(),
                  "Offset to the sample to detector distance (use only when "
                  "using the distance found in the meta data), in mm");

  declareProperty(
      "SolidAngleCorrection", true,
      "If true, the solid angle correction will be applied to the data");
  declareProperty(
      "DetectorTubes", false,
      "If true, the solid angle correction for tube detectors will be applied");
  // -- Define group --
  setPropertyGroup("UseConfigTOFCuts", load_grp);
  setPropertyGroup("LowTOFCut", load_grp);
  setPropertyGroup("HighTOFCut", load_grp);

  setPropertyGroup("WavelengthStep", load_grp);
  setPropertyGroup("UseConfigMask", load_grp);
  setPropertyGroup("UseConfig", load_grp);
  setPropertyGroup("CorrectForFlightPath", load_grp);

  setPropertyGroup("SkipTOFCorrection", load_grp);
  setPropertyGroup("PreserveEvents", load_grp);

  setPropertyGroup("SampleDetectorDistance", load_grp);
  setPropertyGroup("SampleDetectorDistanceOffset", load_grp);
  setPropertyGroup("SolidAngleCorrection", load_grp);
  setPropertyGroup("DetectorTubes", load_grp);
  // Beam center
  std::string center_grp = "Beam Center";
  std::vector<std::string> centerOptions{"None", "Value", "DirectBeam",
                                         "Scattering"};

  declareProperty("BeamCenterMethod", "None",
                  boost::make_shared<StringListValidator>(centerOptions),
                  "Method for determining the data beam center");
  // declareProperty("FindBeamCenter", false, "If True, the beam center will be
  // calculated");
  declareProperty(
      "UseConfigBeam", false,
      "If True, the beam center will be taken from the config file");

  //    Option 1: Set beam center by hand
  declareProperty("BeamCenterX", EMPTY_DBL(),
                  "Position of the beam center, in pixel");
  declareProperty("BeamCenterY", EMPTY_DBL(),
                  "Position of the beam center, in pixel");
  setPropertySettings("BeamCenterX",
                      make_unique<VisibleWhenProperty>("BeamCenterMethod",
                                                       IS_EQUAL_TO, "Value"));
  setPropertySettings("BeamCenterY",
                      make_unique<VisibleWhenProperty>("BeamCenterMethod",
                                                       IS_EQUAL_TO, "Value"));

  //    Option 2: Find it (expose properties from FindCenterOfMass)
  declareProperty(
      make_unique<API::FileProperty>(
          "BeamCenterFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
      "The name of the input event Nexus file to load");
  setPropertySettings("BeamCenterFile",
                      make_unique<VisibleWhenProperty>(
                          "BeamCenterMethod", IS_NOT_EQUAL_TO, "None"));

  // declareProperty("Tolerance", EMPTY_DBL(), "Tolerance on the center of mass
  // position between each iteration [m]. Default: 0.00125");
  auto positiveDouble = boost::make_shared<BoundedValidator<double>>();
  positiveDouble->setLower(0);
  // declareProperty("UseDirectBeamMethod", true, "If true, the direct beam
  // method will be used");
  declareProperty(
      "BeamRadius", EMPTY_DBL(),
      "Radius of the beam area used the exclude the beam when calculating "
      "the center of mass of the scattering pattern [pixels]. Default=3.0");
  setPropertySettings("BeamRadius",
                      make_unique<VisibleWhenProperty>(
                          "BeamCenterMethod", IS_EQUAL_TO, "Scattering"));
  // -- Define group --
  setPropertyGroup("BeamCenterMethod", center_grp);
  setPropertyGroup("UseConfigBeam", center_grp);
  setPropertyGroup("BeamCenterX", center_grp);
  setPropertyGroup("BeamCenterY", center_grp);
  setPropertyGroup("BeamCenterFile", center_grp);
  // setPropertyGroup("Tolerance", center_grp);
  // setPropertyGroup("UseDirectBeamMethod", center_grp);
  setPropertyGroup("BeamRadius", center_grp);

  // Normalisation
  std::string norm_grp = "Normalisation";
  std::vector<std::string> incidentBeamNormOptions;
  incidentBeamNormOptions.emplace_back("None");
  // The data will be normalised to the monitor counts
  incidentBeamNormOptions.emplace_back("Monitor");
  // The data will be normalised to the total charge and divided by the beam
  // profile
  incidentBeamNormOptions.emplace_back("BeamProfileAndCharge");
  // The data will be normalised to the total charge only (no beam profile)
  incidentBeamNormOptions.emplace_back("Charge");
  this->declareProperty(
      "Normalisation", "BeamProfileAndCharge",
      boost::make_shared<StringListValidator>(incidentBeamNormOptions),
      "Options for data normalisation");

  declareProperty("LoadMonitors", false,
                  "If true, the monitor workspace will be loaded");
  // declareProperty("NormaliseToBeam", true, "If true, the data will be
  // normalised to the total charge and divided by the beam profile");
  // declareProperty("NormaliseToMonitor", false, "If true, the data will be
  // normalised to the monitor, otherwise the total charge will be used");
  declareProperty(
      make_unique<API::FileProperty>("MonitorReferenceFile", "",
                                     API::FileProperty::OptionalLoad,
                                     "_event.nxs"),
      "The name of the beam monitor reference file used for normalisation");

  setPropertyGroup("Normalisation", norm_grp);
  setPropertyGroup("LoadMonitors", norm_grp);
  setPropertyGroup("MonitorReferenceFile", norm_grp);

  declareProperty(
      make_unique<API::FileProperty>(
          "DarkCurrentFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
      "The name of the input event Nexus file to load as dark current.");

  // Sensitivity
  std::string eff_grp = "Sensitivity";
  declareProperty(
      make_unique<API::FileProperty>(
          "SensitivityFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
      "Flood field or sensitivity file.");
  declareProperty(
      "MinEfficiency", EMPTY_DBL(), positiveDouble,
      "Minimum efficiency for a pixel to be considered (default: no minimum).");
  declareProperty(
      "MaxEfficiency", EMPTY_DBL(), positiveDouble,
      "Maximum efficiency for a pixel to be considered (default: no maximum).");
  declareProperty("UseDefaultDC", true, "If true, the dark current subtracted "
                                        "from the sample data will also be "
                                        "subtracted from the flood field.");
  declareProperty(make_unique<API::FileProperty>(
                      "SensitivityDarkCurrentFile", "",
                      API::FileProperty::OptionalLoad, "_event.nxs"),
                  "The name of the input file to load as dark current.");
  // - sensitivity beam center
  declareProperty("SensitivityBeamCenterMethod", "None",
                  boost::make_shared<StringListValidator>(centerOptions),
                  "Method for determining the sensitivity data beam center");

  //    Option 1: Set beam center by hand
  declareProperty("SensitivityBeamCenterX", EMPTY_DBL(),
                  "Sensitivity beam center location in X [pixels]");
  setPropertySettings("SensitivityBeamCenterX",
                      make_unique<VisibleWhenProperty>(
                          "SensitivityBeamCenterMethod", IS_EQUAL_TO, "Value"));

  declareProperty("SensitivityBeamCenterY", EMPTY_DBL(),
                  "Sensitivity beam center location in Y [pixels]");
  setPropertySettings("SensitivityBeamCenterY",
                      make_unique<VisibleWhenProperty>(
                          "SensitivityBeamCenterMethod", IS_EQUAL_TO, "Value"));

  //    Option 2: Find it (expose properties from FindCenterOfMass)
  declareProperty(
      make_unique<API::FileProperty>("SensitivityBeamCenterFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "The name of the input data file to load");
  setPropertySettings(
      "SensitivityBeamCenterFile",
      make_unique<VisibleWhenProperty>("SensitivityBeamCenterMethod",
                                       IS_NOT_EQUAL_TO, "None"));
  declareProperty(
      "SensitivityBeamCenterRadius", EMPTY_DBL(),
      "Radius of the beam area used the exclude the beam when calculating "
      "the center of mass of the scattering pattern [pixels]. Default=3.0");
  setPropertySettings("SensitivityBeamCenterRadius",
                      make_unique<VisibleWhenProperty>(
                          "BeamCenterMethod", IS_EQUAL_TO, "Scattering"));
  declareProperty("OutputSensitivityWorkspace", "",
                  "Name to give the sensitivity workspace");
  // -- Define group --
  setPropertyGroup("SensitivityFile", eff_grp);
  setPropertyGroup("MinEfficiency", eff_grp);
  setPropertyGroup("MaxEfficiency", eff_grp);
  setPropertyGroup("UseDefaultDC", eff_grp);
  setPropertyGroup("SensitivityDarkCurrentFile", eff_grp);
  setPropertyGroup("SensitivityBeamCenterMethod", eff_grp);
  setPropertyGroup("SensitivityBeamCenterX", eff_grp);
  setPropertyGroup("SensitivityBeamCenterY", eff_grp);
  setPropertyGroup("SensitivityBeamCenterFile", eff_grp);
  setPropertyGroup("SensitivityBeamCenterRadius", eff_grp);
  setPropertyGroup("OutputSensitivityWorkspace", eff_grp);

  // Transmission
  std::string trans_grp = "Transmission";
  std::vector<std::string> transOptions{"Value", "DirectBeam"};
  declareProperty("TransmissionMethod", "Value",
                  boost::make_shared<StringListValidator>(transOptions),
                  "Transmission determination method");

  // - Transmission value entered by hand
  declareProperty("TransmissionValue", EMPTY_DBL(), positiveDouble,
                  "Transmission value.");
  setPropertySettings("TransmissionValue",
                      make_unique<VisibleWhenProperty>("TransmissionMethod",
                                                       IS_EQUAL_TO, "Value"));
  declareProperty("TransmissionError", EMPTY_DBL(), positiveDouble,
                  "Transmission error.");
  setPropertySettings("TransmissionError",
                      make_unique<VisibleWhenProperty>("TransmissionMethod",
                                                       IS_EQUAL_TO, "Value"));
  // - Direct beam method transmission calculation
  declareProperty(
      "TransmissionBeamRadius", 3.0,
      "Radius of the beam area used to compute the transmission [pixels]");
  setPropertySettings("TransmissionBeamRadius",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty(
      make_unique<API::FileProperty>("TransmissionSampleDataFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "Sample data file for transmission calculation");
  setPropertySettings("TransmissionSampleDataFile",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty(
      make_unique<API::FileProperty>("TransmissionEmptyDataFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "Empty data file for transmission calculation");
  setPropertySettings("TransmissionEmptyDataFile",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty("FitFramesTogether", false,
                  "If true, the two frames will be fit together");
  setPropertySettings("FitFramesTogether",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));

  // - transmission beam center
  declareProperty("TransmissionBeamCenterMethod", "None",
                  boost::make_shared<StringListValidator>(centerOptions),
                  "Method for determining the transmission data beam center");
  setPropertySettings("TransmissionBeamCenterMethod",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));

  //    Option 1: Set beam center by hand
  declareProperty("TransmissionBeamCenterX", EMPTY_DBL(),
                  "Transmission beam center location in X [pixels]");
  setPropertySettings("TransmissionBeamCenterX",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty("TransmissionBeamCenterY", EMPTY_DBL(),
                  "Transmission beam center location in Y [pixels]");
  setPropertySettings("TransmissionBeamCenterY",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));

  //    Option 2: Find it (expose properties from FindCenterOfMass)
  declareProperty(
      make_unique<API::FileProperty>("TransmissionBeamCenterFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "The name of the input data file to load");
  setPropertySettings("TransmissionBeamCenterFile",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));

  declareProperty(
      make_unique<API::FileProperty>("TransmissionDarkCurrentFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "The name of the input data file to load as transmission dark current.");
  setPropertySettings("TransmissionDarkCurrentFile",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_NOT_EQUAL_TO, "Value"));
  declareProperty(
      "TransmissionUseSampleDC", true,
      "If true, the sample dark current will be used IF a dark current file is"
      "not set.");
  setPropertySettings("TransmissionUseSampleDC",
                      make_unique<VisibleWhenProperty>(
                          "TransmissionMethod", IS_NOT_EQUAL_TO, "Value"));
  declareProperty(
      "ThetaDependentTransmission", true,
      "If true, a theta-dependent transmission correction will be applied.");

  // -- Define group --
  setPropertyGroup("TransmissionMethod", trans_grp);
  setPropertyGroup("TransmissionValue", trans_grp);
  setPropertyGroup("TransmissionError", trans_grp);
  setPropertyGroup("TransmissionBeamRadius", trans_grp);
  setPropertyGroup("TransmissionSampleDataFile", trans_grp);
  setPropertyGroup("TransmissionEmptyDataFile", trans_grp);
  setPropertyGroup("FitFramesTogether", trans_grp);
  setPropertyGroup("TransmissionBeamCenterMethod", trans_grp);
  setPropertyGroup("TransmissionBeamCenterX", trans_grp);
  setPropertyGroup("TransmissionBeamCenterY", trans_grp);
  setPropertyGroup("TransmissionBeamCenterFile", trans_grp);

  setPropertyGroup("TransmissionDarkCurrentFile", trans_grp);
  setPropertyGroup("TransmissionUseSampleDC", trans_grp);
  setPropertyGroup("ThetaDependentTransmission", trans_grp);
  // Background options
  std::string bck_grp = "Background";
  declareProperty("BackgroundFiles", "", "Background data files");
  declareProperty("BckTransmissionMethod", "Value",
                  boost::make_shared<StringListValidator>(transOptions),
                  "Transmission determination method");

  // - Transmission value entered by hand
  declareProperty("BckTransmissionValue", EMPTY_DBL(), positiveDouble,
                  "Transmission value.");
  setPropertySettings("BckTransmissionValue",
                      make_unique<VisibleWhenProperty>("BckTransmissionMethod",
                                                       IS_EQUAL_TO, "Value"));

  declareProperty("BckTransmissionError", EMPTY_DBL(), positiveDouble,
                  "Transmission error.");
  setPropertySettings("BckTransmissionError",
                      make_unique<VisibleWhenProperty>("BckTransmissionMethod",
                                                       IS_EQUAL_TO, "Value"));

  // - Direct beam method transmission calculation
  declareProperty(
      "BckTransmissionBeamRadius", 3.0,
      "Radius of the beam area used to compute the transmission [pixels]");
  setPropertySettings("BckTransmissionBeamRadius",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty(
      make_unique<API::FileProperty>("BckTransmissionSampleDataFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "Sample data file for transmission calculation");
  setPropertySettings("BckTransmissionSampleDataFile",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty(
      make_unique<API::FileProperty>("BckTransmissionEmptyDataFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "Empty data file for transmission calculation");
  setPropertySettings("BckTransmissionEmptyDataFile",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty("BckFitFramesTogether", false,
                  "If true, the two frames will be fit together");
  setPropertySettings("BckFitFramesTogether",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));

  // - transmission beam center
  declareProperty("BckTransmissionBeamCenterMethod", "None",
                  boost::make_shared<StringListValidator>(centerOptions),
                  "Method for determining the transmission data beam center");
  setPropertySettings("BckTransmissionBeamCenterMethod",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  //    Option 1: Set beam center by hand
  declareProperty("BckTransmissionBeamCenterX", EMPTY_DBL(),
                  "Transmission beam center location in X [pixels]");
  setPropertySettings("BckTransmissionBeamCenterX",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty("BckTransmissionBeamCenterY", EMPTY_DBL(),
                  "Transmission beam center location in Y [pixels]");
  //    Option 2: Find it (expose properties from FindCenterOfMass)
  setPropertySettings("BckTransmissionBeamCenterY",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty(
      make_unique<API::FileProperty>("BckTransmissionBeamCenterFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "The name of the input data file to load");
  setPropertySettings("BckTransmissionBeamCenterFile",
                      make_unique<VisibleWhenProperty>(
                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
  declareProperty(
      make_unique<API::FileProperty>("BckTransmissionDarkCurrentFile", "",
                                     API::FileProperty::OptionalLoad, ".xml"),
      "The name of the input data file to load as background "
      "transmission dark current.");
  setPropertySettings("BckTransmissionDarkCurrentFile",
                      make_unique<VisibleWhenProperty>("BckTransmissionMethod",
                                                       IS_EQUAL_TO,
                                                       "BeamSpreader"));
  declareProperty(
      "BckThetaDependentTransmission", true,
      "If true, a theta-dependent transmission correction will be applied.");

  setPropertyGroup("BackgroundFiles", bck_grp);
  setPropertyGroup("BckTransmissionMethod", bck_grp);
  setPropertyGroup("BckTransmissionValue", bck_grp);
  setPropertyGroup("BckTransmissionError", bck_grp);
  setPropertyGroup("BckTransmissionBeamRadius", bck_grp);
  setPropertyGroup("BckTransmissionSampleDataFile", bck_grp);
  setPropertyGroup("BckTransmissionEmptyDataFile", bck_grp);
  setPropertyGroup("BckTransmissionBeamCenterMethod", bck_grp);
  setPropertyGroup("BckTransmissionBeamCenterX", bck_grp);
  setPropertyGroup("BckTransmissionBeamCenterY", bck_grp);
  setPropertyGroup("BckTransmissionBeamCenterFile", bck_grp);
  setPropertyGroup("BckTransmissionDarkCurrentFile", bck_grp);
  setPropertyGroup("BckThetaDependentTransmission", bck_grp);

  // Geometry correction
  declareProperty("SampleThickness", EMPTY_DBL(), "Sample thickness [cm]");

  // Masking
  std::string mask_grp = "Mask";
  declareProperty(make_unique<ArrayProperty<int>>("MaskedDetectorList"),
                  "List of detector IDs to be masked");
  declareProperty(
      make_unique<ArrayProperty<int>>("MaskedEdges"),
      "Number of pixels to mask on the edges: X-low, X-high, Y-low, Y-high");
  std::vector<std::string> maskOptions{"None", "Front", "Back"};
  declareProperty("MaskedSide", "None",
                  boost::make_shared<StringListValidator>(maskOptions),
                  "Mask one side of the detector");

  setPropertyGroup("MaskedDetectorList", mask_grp);
  setPropertyGroup("MaskedEdges", mask_grp);
  setPropertyGroup("MaskedSide", mask_grp);

  // Absolute scale
  std::string abs_scale_grp = "Absolute Scale";
  std::vector<std::string> scaleOptions;
  scaleOptions.emplace_back("None");
  scaleOptions.emplace_back("Value");
  scaleOptions.emplace_back("ReferenceData");
  declareProperty("AbsoluteScaleMethod", "None",
                  boost::make_shared<StringListValidator>(scaleOptions),
                  "Absolute scale correction method");
  declareProperty("AbsoluteScalingFactor", 1.0, "Absolute scaling factor");
  setPropertySettings("AbsoluteScalingFactor",
                      make_unique<VisibleWhenProperty>("AbsoluteScaleMethod",
                                                       IS_EQUAL_TO, "Value"));
  declareProperty(
      make_unique<API::FileProperty>("AbsoluteScalingReferenceFilename", "",
                                     API::FileProperty::OptionalLoad, ".xml"));
  setPropertySettings("AbsoluteScalingReferenceFilename",
                      make_unique<VisibleWhenProperty>(
                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));
  declareProperty(
      "AbsoluteScalingBeamDiameter", 0.0,
      "Beamstop diameter for computing the absolute scale factor [mm]. "
      "Read from file if not supplied.");
  setPropertySettings("AbsoluteScalingBeamDiameter",
                      make_unique<VisibleWhenProperty>(
                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));
  declareProperty(
      "AbsoluteScalingAttenuatorTrans", 1.0,
      "Attenuator transmission value for computing the absolute scale factor");
  setPropertySettings("AbsoluteScalingAttenuatorTrans",
                      make_unique<VisibleWhenProperty>(
                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));
  declareProperty("AbsoluteScalingApplySensitivity", false,
                  "Apply sensitivity correction to the reference data "
                  "when computing the absolute scale factor");
  setPropertySettings("AbsoluteScalingApplySensitivity",
                      make_unique<VisibleWhenProperty>(
                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));

  setPropertyGroup("AbsoluteScaleMethod", abs_scale_grp);
  setPropertyGroup("AbsoluteScalingFactor", abs_scale_grp);
  setPropertyGroup("AbsoluteScalingReferenceFilename", abs_scale_grp);
  setPropertyGroup("AbsoluteScalingBeamDiameter", abs_scale_grp);
  setPropertyGroup("AbsoluteScalingAttenuatorTrans", abs_scale_grp);
  setPropertyGroup("AbsoluteScalingApplySensitivity", abs_scale_grp);

  // I(Q) calculation
  std::string iq1d_grp = "I(q) Calculation";
  declareProperty("DoAzimuthalAverage", true);
  auto positiveInt = boost::make_shared<BoundedValidator<int>>();
  positiveInt->setLower(0);
  declareProperty("IQNumberOfBins", 100, positiveInt,
                  "Number of I(q) bins when binning is not specified");
  declareProperty("IQLogBinning", false,
                  "I(q) log binning when binning is not specified");
  declareProperty("IQIndependentBinning", true, "If true and frame skipping is "
                                                "used, each frame will have "
                                                "its own binning");
  declareProperty(
      "IQScaleResults", true,
      "If true and frame skipping is used, frame 1 will be scaled to frame 2");
  declareProperty("ComputeResolution", false,
                  "If true the Q resolution will be computed");
  declareProperty("SampleApertureDiameter", 10.0,
                  "Sample aperture diameter [mm]");

  declareProperty("Do2DReduction", true);
  declareProperty("IQ2DNumberOfBins", 100, positiveInt,
                  "Number of I(qx,qy) bins.");

  // -- Define group --
  setPropertyGroup("DoAzimuthalAverage", iq1d_grp);
  setPropertyGroup("IQNumberOfBins", iq1d_grp);
  setPropertyGroup("IQLogBinning", iq1d_grp);
  setPropertyGroup("IQIndependentBinning", iq1d_grp);
  setPropertyGroup("IQScaleResults", iq1d_grp);
  setPropertyGroup("ComputeResolution", iq1d_grp);
  setPropertyGroup("SampleApertureDiameter", iq1d_grp);
  setPropertyGroup("Do2DReduction", iq1d_grp);
  setPropertyGroup("IQ2DNumberOfBins", iq1d_grp);
  declareProperty("ProcessInfo", "", "Additional process information");
  declareProperty("OutputDirectory", "",
                  "Directory to put the output files in");
  declareProperty("OutputMessage", "", Direction::Output);
  declareProperty("ReductionProperties", "__sans_reduction_properties",
                  Direction::Input);
void SetupEQSANSReduction::exec() {
  // Reduction property manager
  const std::string reductionManagerName = getProperty("ReductionProperties");
  if (reductionManagerName.empty()) {
Hahn, Steven's avatar
Hahn, Steven committed
    g_log.error() << "ERROR: Reduction Property Manager name is empty\n";
  boost::shared_ptr<PropertyManager> reductionManager =
      boost::make_shared<PropertyManager>();
  PropertyManagerDataService::Instance().addOrReplace(reductionManagerName,
                                                      reductionManager);

  // Store name of the instrument
  reductionManager->declareProperty(
      make_unique<PropertyWithValue<std::string>>("InstrumentName", "EQSANS"));
  // Store additional (and optional) process information
  const std::string processInfo = getProperty("ProcessInfo");
  reductionManager->declareProperty(
      Kernel::make_unique<PropertyWithValue<std::string>>("ProcessInfo",
                                                          processInfo));

  // Store the output directory
  const std::string outputDirectory = getProperty("OutputDirectory");
  reductionManager->declareProperty(
      Kernel::make_unique<PropertyWithValue<std::string>>("OutputDirectory",
                                                          outputDirectory));
  // Store normalization algorithm
  const std::string normalization = getProperty("Normalisation");
  bool loadMonitors = getProperty("LoadMonitors");
  const std::string monitorRefFile = getPropertyValue("MonitorReferenceFile");
  // If we normalize to monitor, force the loading of monitor data
  IAlgorithm_sptr normAlg = createChildAlgorithm("EQSANSNormalise");
  if (boost::contains(normalization, "BeamProfileAndCharge")) {
    normAlg->setProperty("NormaliseToBeam", true);
    normAlg->setProperty("BeamSpectrumFile", monitorRefFile);
  } else if (boost::contains(normalization, "Charge")) {
    normAlg->setProperty("NormaliseToBeam", false);
  } else if (boost::contains(normalization, "Monitor")) {
      g_log.error() << "ERROR: normalize-to-monitor was turned ON but no "
                       "reference data was selected\n";
    }
    normAlg->setProperty("NormaliseToMonitor", true);
    normAlg->setProperty("BeamSpectrumFile", monitorRefFile);
  }
  normAlg->setPropertyValue("ReductionProperties", reductionManagerName);
  auto normAlgProp = make_unique<AlgorithmProperty>("NormaliseAlgorithm");
  normAlgProp->setValue(normAlg->toString());
  reductionManager->declareProperty(std::move(normAlgProp));
  IAlgorithm_sptr loadAlg = createChildAlgorithm("EQSANSLoad");
  const bool useConfigBeam = getProperty("UseConfigBeam");
  loadAlg->setProperty("UseConfigBeam", useConfigBeam);
  const bool useConfigTOFCuts = getProperty("UseConfigTOFCuts");
  loadAlg->setProperty("UseConfigTOFCuts", useConfigTOFCuts);
  if (!useConfigTOFCuts) {
    const double lowTOFCut = getProperty("LowTOFCut");
    const double highTOFCut = getProperty("HighTOFCut");
    loadAlg->setProperty("LowTOFCut", lowTOFCut);
    loadAlg->setProperty("HighTOFCut", highTOFCut);
  }

  const bool skipTOFCorrection = getProperty("SkipTOFCorrection");
  loadAlg->setProperty("SkipTOFCorrection", skipTOFCorrection);

  const bool correctForFlightPath = getProperty("CorrectForFlightPath");
  loadAlg->setProperty("CorrectForFlightPath", correctForFlightPath);

  const bool preserveEvents = getProperty("PreserveEvents");
  loadAlg->setProperty("PreserveEvents", preserveEvents);
  loadAlg->setProperty("LoadMonitors", loadMonitors);

  const double sdd = getProperty("SampleDetectorDistance");
  loadAlg->setProperty("SampleDetectorDistance", sdd);
  const double sddOffset = getProperty("SampleDetectorDistanceOffset");
  loadAlg->setProperty("SampleDetectorDistanceOffset", sddOffset);
  const double wlStep = getProperty("WavelengthStep");
  loadAlg->setProperty("WavelengthStep", wlStep);

  const bool useConfig = getProperty("UseConfig");
  loadAlg->setProperty("UseConfig", useConfig);
  const bool useConfigMask = getProperty("UseConfigMask");
  loadAlg->setProperty("UseConfigMask", useConfigMask);
  auto loadAlgProp = make_unique<AlgorithmProperty>("LoadAlgorithm");
  loadAlgProp->setValue(loadAlg->toString());
  reductionManager->declareProperty(std::move(loadAlgProp));
  // Store dark current algorithm
  const std::string darkCurrentFile = getPropertyValue("DarkCurrentFile");
  if (!darkCurrentFile.empty()) {
    IAlgorithm_sptr darkAlg =
        createChildAlgorithm("EQSANSDarkCurrentSubtraction");
    darkAlg->setProperty("Filename", darkCurrentFile);
    darkAlg->setProperty("OutputDarkCurrentWorkspace", "");
    darkAlg->setPropertyValue("ReductionProperties", reductionManagerName);
    auto dcAlgProp = make_unique<AlgorithmProperty>("DarkCurrentAlgorithm");
    dcAlgProp->setValue(darkAlg->toString());
    reductionManager->declareProperty(std::move(dcAlgProp));
  // Store default dark current algorithm
  IAlgorithm_sptr darkDefaultAlg =
      createChildAlgorithm("EQSANSDarkCurrentSubtraction");
  darkDefaultAlg->setProperty("OutputDarkCurrentWorkspace", "");
  darkDefaultAlg->setPropertyValue("ReductionProperties", reductionManagerName);
  auto ddcAlgProp =
      make_unique<AlgorithmProperty>("DefaultDarkCurrentAlgorithm");
  ddcAlgProp->setValue(darkDefaultAlg->toString());
  reductionManager->declareProperty(std::move(ddcAlgProp));
  // Solid angle correction
  const bool solidAngleCorrection = getProperty("SolidAngleCorrection");
  if (solidAngleCorrection) {
    const bool detectorTubes = getProperty("DetectorTubes");
    IAlgorithm_sptr solidAlg = createChildAlgorithm("SANSSolidAngleCorrection");
    solidAlg->setProperty("DetectorTubes", detectorTubes);
    auto ssaAlgProp =
        make_unique<AlgorithmProperty>("SANSSolidAngleCorrection");
    ssaAlgProp->setValue(solidAlg->toString());
    reductionManager->declareProperty(std::move(ssaAlgProp));
  }

  // Beam center
  const double beamCenterX = getProperty("BeamCenterX");
  const double beamCenterY = getProperty("BeamCenterY");
  const std::string centerMethod = getPropertyValue("BeamCenterMethod");

  // Beam center option for transmission data
  if (boost::iequals(centerMethod, "Value")) {
    if (!isEmpty(beamCenterX) && !isEmpty(beamCenterY)) {
      reductionManager->declareProperty(make_unique<PropertyWithValue<double>>(
          "LatestBeamCenterX", beamCenterX));
      reductionManager->declareProperty(make_unique<PropertyWithValue<double>>(
          "LatestBeamCenterY", beamCenterY));
  } else if (!boost::iequals(centerMethod, "None")) {
    bool useDirectBeamMethod = true;
    if (!boost::iequals(centerMethod, "DirectBeam"))
      useDirectBeamMethod = false;
    const std::string beamCenterFile = getProperty("BeamCenterFile");
    if (!beamCenterFile.empty()) {
      const double beamRadius = getProperty("BeamRadius");

      IAlgorithm_sptr ctrAlg = createChildAlgorithm("SANSBeamFinder");
      ctrAlg->setProperty("Filename", beamCenterFile);
      ctrAlg->setProperty("UseDirectBeamMethod", useDirectBeamMethod);
      if (!isEmpty(beamRadius))
        ctrAlg->setProperty("BeamRadius", beamRadius);
      ctrAlg->setPropertyValue("ReductionProperties", reductionManagerName);

      auto ctrAlgProp =
          make_unique<AlgorithmProperty>("SANSBeamFinderAlgorithm");
      ctrAlgProp->setValue(ctrAlg->toString());
      reductionManager->declareProperty(std::move(ctrAlgProp));
    } else {
      g_log.error() << "ERROR: Beam center determination was required"
                       " but no file was provided\n";
  // Sensitivity correction, transmission and background
  setupSensitivity(reductionManager);
  setupTransmission(reductionManager);
  setupBackground(reductionManager);
  // Geometry correction
  const double thickness = getProperty("SampleThickness");
  if (!isEmpty(thickness)) {
    IAlgorithm_sptr thickAlg = createChildAlgorithm("NormaliseByThickness");
    thickAlg->setProperty("SampleThickness", thickness);

    auto geomAlgProp = make_unique<AlgorithmProperty>("GeometryAlgorithm");
    geomAlgProp->setValue(thickAlg->toString());
    reductionManager->declareProperty(std::move(geomAlgProp));
  }

  // Mask
  const std::string maskDetList = getPropertyValue("MaskedDetectorList");
  const std::string maskEdges = getPropertyValue("MaskedEdges");
  const std::string maskSide = getProperty("MaskedSide");

  IAlgorithm_sptr maskAlg = createChildAlgorithm("SANSMask");
  // The following is broken, try PropertyValue
  maskAlg->setPropertyValue("Facility", "SNS");
  maskAlg->setPropertyValue("MaskedDetectorList", maskDetList);
  maskAlg->setPropertyValue("MaskedEdges", maskEdges);
  maskAlg->setProperty("MaskedSide", maskSide);
  auto maskAlgProp = make_unique<AlgorithmProperty>("MaskAlgorithm");
  maskAlgProp->setValue(maskAlg->toString());
  reductionManager->declareProperty(std::move(maskAlgProp));

  // Absolute scaling
  const std::string absScaleMethod = getProperty("AbsoluteScaleMethod");
  if (boost::iequals(absScaleMethod, "Value")) {
    const double absScaleFactor = getProperty("AbsoluteScalingFactor");

    IAlgorithm_sptr absAlg = createChildAlgorithm("SANSAbsoluteScale");
    absAlg->setProperty("Method", absScaleMethod);
    absAlg->setProperty("ScalingFactor", absScaleFactor);
    absAlg->setPropertyValue("ReductionProperties", reductionManagerName);
    auto absAlgProp = make_unique<AlgorithmProperty>("AbsoluteScaleAlgorithm");
    absAlgProp->setValue(absAlg->toString());
    reductionManager->declareProperty(std::move(absAlgProp));
  } else if (boost::iequals(absScaleMethod, "ReferenceData")) {
    const std::string absRefFile =
        getPropertyValue("AbsoluteScalingReferenceFilename");
    const double beamDiam = getProperty("AbsoluteScalingBeamDiameter");
    const double attTrans = getProperty("AbsoluteScalingAttenuatorTrans");
    const bool applySensitivity =
        getProperty("AbsoluteScalingApplySensitivity");

    IAlgorithm_sptr absAlg = createChildAlgorithm("SANSAbsoluteScale");
    absAlg->setProperty("Method", absScaleMethod);
    absAlg->setProperty("ReferenceDataFilename", absRefFile);
    absAlg->setProperty("BeamstopDiameter", beamDiam);
    absAlg->setProperty("AttenuatorTransmission", attTrans);
    absAlg->setProperty("ApplySensitivity", applySensitivity);
    absAlg->setPropertyValue("ReductionProperties", reductionManagerName);
    auto scaleAlgProp =
        make_unique<AlgorithmProperty>("AbsoluteScaleAlgorithm");
    scaleAlgProp->setValue(absAlg->toString());
    reductionManager->declareProperty(std::move(scaleAlgProp));
  // Azimuthal averaging
  const bool doAveraging = getProperty("DoAzimuthalAverage");
  if (doAveraging) {
    const std::string nBins = getPropertyValue("IQNumberOfBins");
    const bool logBinning = getProperty("IQLogBinning");
    const double sampleApert = getProperty("SampleApertureDiameter");
    const bool computeResolution = getProperty("ComputeResolution");
    const bool indepBinning = getProperty("IQIndependentBinning");
    const bool scaleResults = getProperty("IQScaleResults");

    IAlgorithm_sptr iqAlg = createChildAlgorithm("EQSANSAzimuthalAverage1D");
    iqAlg->setPropertyValue("NumberOfBins", nBins);
    iqAlg->setProperty("LogBinning", logBinning);
    iqAlg->setProperty("ScaleResults", scaleResults);
    iqAlg->setProperty("ComputeResolution", computeResolution);
    iqAlg->setProperty("IndependentBinning", indepBinning);
    iqAlg->setProperty("SampleApertureDiameter", sampleApert);
    iqAlg->setPropertyValue("ReductionProperties", reductionManagerName);

    auto iqalgProp = make_unique<AlgorithmProperty>("IQAlgorithm");
    iqalgProp->setValue(iqAlg->toString());
    reductionManager->declareProperty(std::move(iqalgProp));
  // 2D reduction
  const bool do2DReduction = getProperty("Do2DReduction");
  if (do2DReduction) {
    const std::string n_bins = getPropertyValue("IQ2DNumberOfBins");
    IAlgorithm_sptr iqAlg = createChildAlgorithm("EQSANSQ2D");
    iqAlg->setPropertyValue("NumberOfBins", n_bins);
    auto xyalgProp = make_unique<AlgorithmProperty>("IQXYAlgorithm");
    xyalgProp->setValue(iqAlg->toString());
    reductionManager->declareProperty(std::move(xyalgProp));
  setPropertyValue("OutputMessage", "EQSANS reduction options set");

  // Save a string representation of this algorithm
  auto setupAlgProp = make_unique<AlgorithmProperty>("SetupAlgorithm");
  setupAlgProp->setValue(toString());
  reductionManager->declareProperty(std::move(setupAlgProp));
void SetupEQSANSReduction::setupSensitivity(
    boost::shared_ptr<PropertyManager> reductionManager) {
  const std::string reductionManagerName = getProperty("ReductionProperties");

  const std::string sensitivityFile = getPropertyValue("SensitivityFile");
  if (!sensitivityFile.empty()) {
    const bool useSampleDC = getProperty("UseDefaultDC");
    const std::string sensitivityDarkCurrentFile =
        getPropertyValue("SensitivityDarkCurrentFile");
    const std::string outputSensitivityWS =
        getPropertyValue("OutputSensitivityWorkspace");
    const double minEff = getProperty("MinEfficiency");
    const double maxEff = getProperty("MaxEfficiency");
    const double sensitivityBeamCenterX = getProperty("SensitivityBeamCenterX");
    const double sensitivityBeamCenterY = getProperty("SensitivityBeamCenterY");

    IAlgorithm_sptr effAlg = createChildAlgorithm("SANSSensitivityCorrection");
    effAlg->setProperty("Filename", sensitivityFile);
    effAlg->setProperty("UseSampleDC", useSampleDC);
    effAlg->setProperty("DarkCurrentFile", sensitivityDarkCurrentFile);
    effAlg->setProperty("MinEfficiency", minEff);
    effAlg->setProperty("MaxEfficiency", maxEff);

    // Beam center option for sensitivity data
    const std::string centerMethod =
        getPropertyValue("SensitivityBeamCenterMethod");
    if (boost::iequals(centerMethod, "Value")) {
      if (!isEmpty(sensitivityBeamCenterX) &&
          !isEmpty(sensitivityBeamCenterY)) {
        effAlg->setProperty("BeamCenterX", sensitivityBeamCenterX);
        effAlg->setProperty("BeamCenterY", sensitivityBeamCenterY);
      }
    } else if (boost::iequals(centerMethod, "DirectBeam") ||
               boost::iequals(centerMethod, "Scattering")) {
      const std::string beamCenterFile =
          getProperty("SensitivityBeamCenterFile");
      const double sensitivityBeamRadius =
          getProperty("SensitivityBeamCenterRadius");
      bool useDirectBeam = boost::iequals(centerMethod, "DirectBeam");
      if (!beamCenterFile.empty()) {
        IAlgorithm_sptr ctrAlg = createChildAlgorithm("SANSBeamFinder");
        ctrAlg->setProperty("Filename", beamCenterFile);
        ctrAlg->setProperty("UseDirectBeamMethod", useDirectBeam);
        ctrAlg->setProperty("PersistentCorrection", false);
        if (!isEmpty(sensitivityBeamRadius))
          ctrAlg->setProperty("BeamRadius", sensitivityBeamRadius);
        ctrAlg->setPropertyValue("ReductionProperties", reductionManagerName);

        auto sensAlgProp =
            make_unique<AlgorithmProperty>("SensitivityBeamCenterAlgorithm");
        sensAlgProp->setValue(ctrAlg->toString());
        reductionManager->declareProperty(std::move(sensAlgProp));
      } else {
        g_log.error()
            << "ERROR: Sensitivity beam center determination was required"
               " but no file was provided\n";
    effAlg->setPropertyValue("OutputSensitivityWorkspace", outputSensitivityWS);
    effAlg->setPropertyValue("ReductionProperties", reductionManagerName);

    auto algProp = make_unique<AlgorithmProperty>("SensitivityAlgorithm");
    algProp->setValue(effAlg->toString());
    reductionManager->declareProperty(std::move(algProp));
void SetupEQSANSReduction::setupTransmission(
    boost::shared_ptr<PropertyManager> reductionManager) {
  const std::string reductionManagerName = getProperty("ReductionProperties");
  // Transmission options
  const bool thetaDependentTrans = getProperty("ThetaDependentTransmission");
  const std::string transMethod = getProperty("TransmissionMethod");
  const std::string darkCurrent =
      getPropertyValue("TransmissionDarkCurrentFile");
  const bool useSampleDC = getProperty("TransmissionUseSampleDC");

  // Transmission is entered by hand
  if (boost::iequals(transMethod, "Value")) {
    const double transValue = getProperty("TransmissionValue");
    const double transError = getProperty("TransmissionError");
    if (!isEmpty(transValue) && !isEmpty(transError)) {
      IAlgorithm_sptr transAlg =
          createChildAlgorithm("ApplyTransmissionCorrection");
      transAlg->setProperty("TransmissionValue", transValue);
      transAlg->setProperty("TransmissionError", transError);
      transAlg->setProperty("ThetaDependent", thetaDependentTrans);

      auto algProp = make_unique<AlgorithmProperty>("TransmissionAlgorithm");
      algProp->setValue(transAlg->toString());
      reductionManager->declareProperty(std::move(algProp));
      g_log.information(
          "SetupEQSANSReduction [TransmissionAlgorithm]:"
          "expected transmission/error values and got empty values");
    }
  }
  // Direct beam method for transmission determination
  else if (boost::iequals(transMethod, "DirectBeam")) {
    const std::string sampleFilename =
        getPropertyValue("TransmissionSampleDataFile");
    const std::string emptyFilename =
        getPropertyValue("TransmissionEmptyDataFile");
    const double beamRadius = getProperty("TransmissionBeamRadius");
    const bool fitFramesTogether = getProperty("FitFramesTogether");
    const double beamX = getProperty("TransmissionBeamCenterX");
    const double beamY = getProperty("TransmissionBeamCenterY");
    const std::string centerMethod =
        getPropertyValue("TransmissionBeamCenterMethod");
    IAlgorithm_sptr transAlg =
        createChildAlgorithm("EQSANSDirectBeamTransmission");
    transAlg->setProperty("FitFramesTogether", fitFramesTogether);
    transAlg->setProperty("SampleDataFilename", sampleFilename);
    transAlg->setProperty("EmptyDataFilename", emptyFilename);
    transAlg->setProperty("BeamRadius", beamRadius);
    transAlg->setProperty("DarkCurrentFilename", darkCurrent);
    transAlg->setProperty("UseSampleDarkCurrent", useSampleDC);

    // Beam center option for transmission data
    if (boost::iequals(centerMethod, "Value") && !isEmpty(beamX) &&
        !isEmpty(beamY)) {
      transAlg->setProperty("BeamCenterX", beamX);
      transAlg->setProperty("BeamCenterY", beamY);
    } else if (boost::iequals(centerMethod, "DirectBeam")) {
      const std::string beamCenterFile =
          getProperty("TransmissionBeamCenterFile");
      if (!beamCenterFile.empty()) {
        IAlgorithm_sptr ctrAlg = createChildAlgorithm("SANSBeamFinder");
        ctrAlg->setProperty("Filename", beamCenterFile);
        ctrAlg->setProperty("UseDirectBeamMethod", true);
        ctrAlg->setProperty("PersistentCorrection", false);
        ctrAlg->setPropertyValue("ReductionProperties", reductionManagerName);

        auto algProp =
            make_unique<AlgorithmProperty>("TransmissionBeamCenterAlgorithm");
        algProp->setValue(ctrAlg->toString());
        reductionManager->declareProperty(std::move(algProp));
      } else {
        g_log.error()
            << "ERROR: Transmission beam center determination was required"
               " but no file was provided\n";
    }
    transAlg->setProperty("ThetaDependent", thetaDependentTrans);
    auto algProp = make_unique<AlgorithmProperty>("TransmissionAlgorithm");
    algProp->setValue(transAlg->toString());
    reductionManager->declareProperty(std::move(algProp));