Skip to content
Snippets Groups Projects
HFIRLoad.cpp 13.1 KiB
Newer Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidWorkflowAlgorithms/HFIRLoad.h"
#include "MantidWorkflowAlgorithms/HFIRInstrument.h"
#include "Poco/NumberFormatter.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidAPI/AlgorithmProperty.h"
#include "MantidAPI/PropertyManagerDataService.h"
#include "MantidAPI/FileProperty.h"
#include "MantidKernel/PropertyManager.h"
namespace Mantid {
namespace WorkflowAlgorithms {

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

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

void HFIRLoad::init() {
  declareProperty(
      new API::FileProperty("Filename", "", API::FileProperty::Load, ".xml"),
      "The name of the input file to load");
  declareProperty(
      new WorkspaceProperty<>("OutputWorkspace", "", Direction::Output),
      "Then name of the output workspace");
  declareProperty(
      "NoBeamCenter", false,
      "If true, the detector will not be moved according to the beam center");
  declareProperty("BeamCenterX", EMPTY_DBL(),
                  "Beam position in X pixel coordinates");
  declareProperty("BeamCenterY", EMPTY_DBL(),
                  "Beam position in Y pixel coordinates");
  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."
                  "Not used when SampleDetectorDistance is provided.");
  // Optionally, we can specify the wavelength and wavelength spread and
  // overwrite
  // the value in the data file (used when the data file is not populated)
  auto mustBePositive = boost::make_shared<Kernel::BoundedValidator<double>>();
  mustBePositive->setLower(0.0);
  declareProperty(
      "Wavelength", EMPTY_DBL(), mustBePositive,
      "Wavelength value to use when loading the data file (Angstrom).");
  declareProperty(
      "WavelengthSpread", 0.1, mustBePositive,
      "Wavelength spread to use when loading the data file (default 0.0)");
  declareProperty("OutputMessage", "", Direction::Output);
  declareProperty("ReductionProperties", "__sans_reduction_properties",
                  Direction::Input);
}

/// Move the detector according to the beam center
void HFIRLoad::moveToBeamCenter(API::MatrixWorkspace_sptr &dataWS,
                                double &center_x, double &center_y) {
  double default_ctr_x_pix = 0.0;
  double default_ctr_y_pix = 0.0;
  double default_ctr_x = 0.0;
  double default_ctr_y = 0.0;
  HFIRInstrument::getDefaultBeamCenter(dataWS, default_ctr_x_pix,
                                       default_ctr_y_pix);
  HFIRInstrument::getCoordinateFromPixel(default_ctr_x_pix, default_ctr_y_pix,
                                         dataWS, default_ctr_x, default_ctr_y);
  // Check that we have a beam center defined, otherwise set the
  // default beam center
  if (isEmpty(center_x) || isEmpty(center_y)) {
    center_x = default_ctr_x_pix;
    center_y = default_ctr_y_pix;
    g_log.information() << "Setting beam center to ["
                        << Poco::NumberFormatter::format(center_x, 1) << ", "
                        << Poco::NumberFormatter::format(center_y, 1) << "]"
                        << std::endl;
    return;
  }

  double beam_ctr_x = 0.0;
  double beam_ctr_y = 0.0;
  HFIRInstrument::getCoordinateFromPixel(center_x, center_y, dataWS, beam_ctr_x,
                                         beam_ctr_y);
  IAlgorithm_sptr mvAlg =
      createChildAlgorithm("MoveInstrumentComponent", 0.5, 0.50);
  mvAlg->setProperty<MatrixWorkspace_sptr>("Workspace", dataWS);
  mvAlg->setProperty("ComponentName", "detector1");
  mvAlg->setProperty("X", default_ctr_x - beam_ctr_x);
  mvAlg->setProperty("Y", default_ctr_y - beam_ctr_y);
  mvAlg->setProperty("RelativePosition", true);
  mvAlg->executeAsChildAlg();
  g_log.information() << "Moving beam center to " << center_x << " " << center_y
                      << std::endl;
void HFIRLoad::exec() {
  // Reduction property manager
  const std::string reductionManagerName = getProperty("ReductionProperties");
  boost::shared_ptr<PropertyManager> reductionManager;
  if (PropertyManagerDataService::Instance().doesExist(reductionManagerName)) {
    reductionManager =
        PropertyManagerDataService::Instance().retrieve(reductionManagerName);
  } else {
    reductionManager = boost::make_shared<PropertyManager>();
    PropertyManagerDataService::Instance().addOrReplace(reductionManagerName,
                                                        reductionManager);
  }

  // If the load algorithm isn't in the reduction properties, add it
  if (!reductionManager->existsProperty("LoadAlgorithm")) {
    AlgorithmProperty *algProp = new AlgorithmProperty("LoadAlgorithm");
    algProp->setValue(toString());
    reductionManager->declareProperty(algProp);
  }

  const std::string fileName = getPropertyValue("Filename");

  // Output log
  const double wavelength_input = getProperty("Wavelength");
  const double wavelength_spread_input = getProperty("WavelengthSpread");

  IAlgorithm_sptr loadAlg = createChildAlgorithm("LoadSpice2D", 0, 0.2);
  loadAlg->setProperty("Filename", fileName);
  if (!isEmpty(wavelength_input)) {
    loadAlg->setProperty("Wavelength", wavelength_input);
    loadAlg->setProperty("WavelengthSpread", wavelength_spread_input);
  }
    loadAlg->executeAsChildAlg();
  } catch (...) {
    // The only way HFIR SANS can load Nexus files is if it's loading data that
    // has already
Doucet, Mathieu's avatar
Doucet, Mathieu committed
    // been processed. This will only happen with sensitivity data.
    // So if we make it here and are still unable to load the file, assume it's
    // a sensitivity file.
    // This will cover the special case where the instrument scientist uses a
    // reduced data set
Doucet, Mathieu's avatar
Doucet, Mathieu committed
    // as a sensitivity data set.
    g_log.warning() << "Unable to load file as a SPICE file. Trying to load as "
                       "a Nexus file." << std::endl;
    loadAlg = createChildAlgorithm("Load", 0, 0.2);
    loadAlg->setProperty("Filename", fileName);
    loadAlg->executeAsChildAlg();
    Workspace_sptr dataWS_tmp = loadAlg->getProperty("OutputWorkspace");
    MatrixWorkspace_sptr dataWS =
        boost::dynamic_pointer_cast<MatrixWorkspace>(dataWS_tmp);
    dataWS->mutableRun().addProperty("is_sensitivity", 1, "", true);
    setProperty<MatrixWorkspace_sptr>("OutputWorkspace", dataWS);
    g_log.notice() << "Successfully loaded " << fileName
                   << " and setting sensitivity flag to True" << std::endl;
  Workspace_sptr dataWS_tmp = loadAlg->getProperty("OutputWorkspace");
  API::MatrixWorkspace_sptr dataWS =
      boost::dynamic_pointer_cast<MatrixWorkspace>(dataWS_tmp);

  // Get the sample-detector distance
  // If SampleDetectorDistance is provided, use it!
  // Otherwise get's "sample-detector-distance" from the data file
  // And uses SampleDetectorDistanceOffset if given!
  const double sample_det_dist = getProperty("SampleDetectorDistance");
  if (!isEmpty(sample_det_dist)) {
    sdd = sample_det_dist;
  } else {
    const std::string sddName = "sample-detector-distance";
    Mantid::Kernel::Property *prop = dataWS->run().getProperty(sddName);
    Mantid::Kernel::PropertyWithValue<double> *dp =
        dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(prop);
    if (!dp) {
      throw std::runtime_error("Could not cast (interpret) the property " +
                               sddName + " as a floating point numeric value.");
    }
    sdd = *dp;

    // Modify SDD according to offset if given
    const double sample_det_offset =
        getProperty("SampleDetectorDistanceOffset");
    if (!isEmpty(sample_det_offset)) {
      sdd += sample_det_offset;
    }
  }
  dataWS->mutableRun().addProperty("sample_detector_distance", sdd, "mm", true);

  progress.report("MoveInstrumentComponent...");

  // Move the detector to its correct position
  IAlgorithm_sptr mvAlg =
      createChildAlgorithm("MoveInstrumentComponent", 0.2, 0.4);
  mvAlg->setProperty<MatrixWorkspace_sptr>("Workspace", dataWS);
  mvAlg->setProperty("ComponentName", "detector1");
  mvAlg->setProperty("Z", sdd / 1000.0);
  mvAlg->setProperty("RelativePosition", false);
  mvAlg->executeAsChildAlg();
  g_log.information() << "Moving detector to " << sdd / 1000.0 << std::endl;
  output_message += "   Detector position: " +
                    Poco::NumberFormatter::format(sdd / 1000.0, 3) + " m\n";

  // Compute beam diameter at the detector
  double src_to_sample = 0.0;
    src_to_sample = HFIRInstrument::getSourceToSampleDistance(dataWS);
    dataWS->mutableRun().addProperty("source-sample-distance", src_to_sample,
                                     "mm", true);
    output_message += "   Computed SSD from number of guides: " +
                      Poco::NumberFormatter::format(src_to_sample / 1000.0, 3) +
                      " \n";
    Mantid::Kernel::Property *prop =
        dataWS->run().getProperty("source-sample-distance");
    Mantid::Kernel::PropertyWithValue<double> *dp =
        dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(prop);
        "   Could not compute SSD from number of guides, taking: " +
        Poco::NumberFormatter::format(src_to_sample / 1000.0, 3) + " \n";
  const std::string sampleADName = "sample-aperture-diameter";
  Mantid::Kernel::Property *prop = dataWS->run().getProperty(sampleADName);
  Mantid::Kernel::PropertyWithValue<double> *dp =
      dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(prop);
  if (!dp) {
    throw std::runtime_error("Could not cast (interpret) the property " +
                             sampleADName +
                             " as a floating point numeric value.");
Doucet, Mathieu's avatar
Doucet, Mathieu committed
  double sample_apert = *dp;
  const std::string sourceADName = "source-aperture-diameter";
  prop = dataWS->run().getProperty(sourceADName);
  dp = dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(prop);
  if (!dp) {
    throw std::runtime_error("Could not cast (interpret) the property " +
                             sourceADName +
                             " as a floating point numeric value.");
Doucet, Mathieu's avatar
Doucet, Mathieu committed
  double source_apert = *dp;
  const double beam_diameter =
      sdd / src_to_sample * (source_apert + sample_apert) + sample_apert;
  dataWS->mutableRun().addProperty("beam-diameter", beam_diameter, "mm", true);

  progress.report("Move to center beam...");

  double center_x = 0;
  double center_y = 0;

  // Move the beam center to its proper position
  const bool noBeamCenter = getProperty("NoBeamCenter");
  if (!noBeamCenter) {
    center_x = getProperty("BeamCenterX");
    center_y = getProperty("BeamCenterY");
    if (isEmpty(center_x) && isEmpty(center_y)) {
      if (reductionManager->existsProperty("LatestBeamCenterX") &&
          reductionManager->existsProperty("LatestBeamCenterY")) {
        center_x = reductionManager->getProperty("LatestBeamCenterX");
        center_y = reductionManager->getProperty("LatestBeamCenterY");

    moveToBeamCenter(dataWS, center_x, center_y);

    progress.report();
    // Add beam center to reduction properties, as the last beam center position
    // that was used.
    // This will give us our default position next time.
    if (!reductionManager->existsProperty("LatestBeamCenterX"))
      reductionManager->declareProperty(
          new PropertyWithValue<double>("LatestBeamCenterX", center_x));
      reductionManager->setProperty("LatestBeamCenterX", center_x);
    if (!reductionManager->existsProperty("LatestBeamCenterY"))
      reductionManager->declareProperty(
          new PropertyWithValue<double>("LatestBeamCenterY", center_y));
      reductionManager->setProperty("LatestBeamCenterY", center_y);

    dataWS->mutableRun().addProperty("beam_center_x", center_x, "pixel", true);
    dataWS->mutableRun().addProperty("beam_center_y", center_y, "pixel", true);
    output_message += "   Beam center: " +
                      Poco::NumberFormatter::format(center_x, 1) + ", " +
                      Poco::NumberFormatter::format(center_y, 1) + "\n";
    HFIRInstrument::getDefaultBeamCenter(dataWS, center_x, center_y);

    dataWS->mutableRun().addProperty("beam_center_x", center_x, "pixel", true);
    dataWS->mutableRun().addProperty("beam_center_y", center_y, "pixel", true);
    output_message += "   Default beam center: " +
                      Poco::NumberFormatter::format(center_x, 1) + ", " +
                      Poco::NumberFormatter::format(center_y, 1) + "\n";
  setProperty<MatrixWorkspace_sptr>(
      "OutputWorkspace", boost::dynamic_pointer_cast<MatrixWorkspace>(dataWS));
  setPropertyValue("OutputMessage", output_message);
}

} // namespace WorkflowAlgorithms
} // namespace Mantid