Skip to content
Snippets Groups Projects
GetSpiceDataRawCountsFromMD.cpp 18.6 KiB
Newer Older
#include "MantidMDAlgorithms/GetSpiceDataRawCountsFromMD.h"

#include "MantidAPI/IMDEventWorkspace.h"
#include "MantidAPI/WorkspaceProperty.h"
#include "MantidKernel/ListValidator.h"
#include "MantidAPI/IMDIterator.h"

namespace Mantid {
namespace MDAlgorithms {
using namespace Mantid::API;
using namespace Mantid::Kernel;

DECLARE_ALGORITHM(GetSpiceDataRawCountsFromMD)

//----------------------------------------------------------------------------------------------
/** Constructor
 */
GetSpiceDataRawCountsFromMD::GetSpiceDataRawCountsFromMD() {}

//----------------------------------------------------------------------------------------------
/** Destructor
  */
GetSpiceDataRawCountsFromMD::~GetSpiceDataRawCountsFromMD() {}

//----------------------------------------------------------------------------------------------
/** Initialization
 * @brief GetSpiceDataRawCountsFromMD::init
 */
void GetSpiceDataRawCountsFromMD::init() {
  declareProperty(new WorkspaceProperty<IMDEventWorkspace>("InputWorkspace", "",
                                                           Direction::Input),
                  "Name of the input data MDEventWorkspace from which the raw "
                  "values are retrieved.");

  declareProperty(new WorkspaceProperty<IMDEventWorkspace>(
                      "MonitorWorkspace", "", Direction::Input),
                  "Name of the input monitor MDEventWorkspace paired with "
                  "input data workspace. ");

  declareProperty(
      new WorkspaceProperty<MatrixWorkspace>("OutputWorkspace", "",
                                             Direction::Output),
      "Name of the output MatrixWorkspace containing the raw data required.");

  std::vector<std::string> vecmode;
  vecmode.push_back("Pt.");
  vecmode.push_back("Detector");
  vecmode.push_back("Sample Log");
  auto modevalidator = boost::make_shared<ListValidator<std::string>>(vecmode);
  declareProperty(
      "Mode", "Detector", modevalidator,
      "Mode selector.  (1) Pt.: get the raw detectors' signal of the "
      "specified Pt.; "
      "(2) Detector: get one detector's signals along all experiment points; "
      "(3) Sample Log: get the value of a sample log along all experiment "
      "points.");

  declareProperty(
      "XLabel", "",
      "Label for the X-values of the output MatrixWorkspace. "
      "For mode 'Pt.', it won't take value.  The output of X-axis is always "
      "2-theta. "
      "For mode 'Detector', if it is left blank, "
      "the default then will be 2-theta value of detector's position. "
      "For mode 'Sample Log', the default value is 'Pt.'. "
      "In the later 2 modes, XLabel can be any supported sample log's name.");

  declareProperty(
      "Experiment point number (i.e., run_number in MDEventWorkspace "
      "of the detectors' counts to be exported. "
      "It is used in mode 'Pt.' only. ");

  declareProperty("DetectorID", EMPTY_INT(),
                  "Detector ID of the detector whose raw counts "
                  "will be exported for all experiment points."
                  "It is used in mode 'Detector' only. ");

  declareProperty("SampleLogName", "",
                  "Name of the sample log whose value to be exported. "
                  "It is used in mode 'Sample Log' only. ");

  declareProperty("NormalizeByMonitorCounts", true,
                  "If specified as true, all the output detectors' counts will "
                  "be normalized by their monitor counts. ");
}

//----------------------------------------------------------------------------------------------
/** Execution body
 * @brief GetSpiceDataRawCountsFromMD::exec
 */
void GetSpiceDataRawCountsFromMD::exec() {
  // Process input parameters
  IMDEventWorkspace_sptr datamdws = getProperty("InputWorkspace");
  IMDEventWorkspace_sptr monitormdws = getProperty("MonitorWorkspace");
  std::string mode = getProperty("Mode");
  bool donormalize = getProperty("NormalizeByMonitorCounts");
  std::string xlabel = getProperty("XLabel");

  // Branch out by mode
  std::vector<double> vecX;
  std::vector<double> vecY;
  std::string ylabel("");
  if (mode.compare("Pt.") == 0) {
    // export detector counts for one specific Pt./run number
    int runnumber = getProperty("Pt");
    if (isEmpty(runnumber))
      throw std::runtime_error("For 'Pt.', value of 'Pt.' must be specified.");
    exportDetCountsOfRun(datamdws, monitormdws, runnumber, vecX, vecY, xlabel,
                         ylabel, donormalize);
  } else if (mode.compare("Detector") == 0) {
    int detid = getProperty("DetectorID");
    if (isEmpty(detid))
      throw std::runtime_error(
          "For mode 'Detector', value of 'DetectorID' must be specified.");
    exportIndividualDetCounts(datamdws, monitormdws, detid, vecX, vecY, xlabel,
                              ylabel, donormalize);
  } else if (mode.compare("Sample Log") == 0) {
    std::string samplelogname = getProperty("SampleLogName");
    if (samplelogname.size() == 0)
      throw std::runtime_error(
          "For mode 'Sample Log', value of 'SampleLogName' must be specified.");
    exportSampleLogValue(datamdws, samplelogname, vecX, vecY, xlabel, ylabel);
  } else {
    // Raise exception
    std::stringstream ess;
    ess << "Mode " << mode << " is not supported.";
    throw std::runtime_error(ess.str());
  }

  // Create and export output workspace
  MatrixWorkspace_sptr outws =
      createOutputWorkspace(vecX, vecY, xlabel, ylabel);
  setProperty("OutputWorkspace", outws);

  return;
}

//----------------------------------------------------------------------------------------------
/** Export all detectors' counts of a particular run
 * @brief GetSpiceDataRawCountsFromMD::exportDetCountsOfRun
 * @param datamdws
 * @param monitormdws
 * @param runnumber
 * @param vecX
 * @param vecY
 * @param xlabel
 * @param ylabel
 * @param donormalize
 */
void GetSpiceDataRawCountsFromMD::exportDetCountsOfRun(
    API::IMDEventWorkspace_const_sptr datamdws,
    API::IMDEventWorkspace_const_sptr monitormdws, const int runnumber,
    std::vector<double> &vecX, std::vector<double> &vecY, std::string &xlabel,
    std::string &ylabel, bool donormalize) {
  // Get detector counts
  std::vector<double> vec2theta;
  std::vector<double> vecDetCounts;
  int detid = -1;
  getDetCounts(datamdws, runnumber, detid, vec2theta, vecDetCounts, true);
  if (vec2theta.size() != vecDetCounts.size())
    throw std::runtime_error(
        "Logic error! Vector of 2theta must have same size as "
        "vector of detectors' counts.");

  // Get monitor counts
  std::vector<double> vec2thetaMon;
  std::vector<double> vecMonitorCounts;
  if (donormalize)
    getDetCounts(monitormdws, runnumber, detid, vec2thetaMon, vecMonitorCounts,
                 false);

  // Normalize if required
  if (donormalize) {
    // check
    if (vecDetCounts.size() != vecMonitorCounts.size())
      throw std::runtime_error(
          "Number of detectors' counts' is different from that of "
          "monitor counts.");

    for (size_t i = 0; i < vecDetCounts.size(); ++i)
      if (vecMonitorCounts[i] > 1.0E-9)
        vecDetCounts[i] = vecDetCounts[i] / vecMonitorCounts[i];
      else
        vecDetCounts[i] = 0.;
  }

  // Sort and output
  const size_t numdets = vecDetCounts.size();
  std::vector<std::pair<double, double>> vecPair(numdets);
  for (size_t i = 0; i < numdets; ++i) {
    vecPair[i] = std::make_pair(vec2theta[i], vecDetCounts[i]);
  }
  std::sort(vecPair.begin(), vecPair.end());

  // Apply to output
  vecX.resize(numdets);
  vecY.resize(numdets);
  for (size_t i = 0; i < numdets; ++i) {
    vecX[i] = vecPair[i].first;
    vecY[i] = vecPair[i].second;
  }

  // Set up label
  xlabel = "Degrees";
  if (donormalize)
    ylabel = "Noramlized Intensity";
  else
    ylabel = "Counts";
}

//----------------------------------------------------------------------------------------------
/** Export a detector's counts accross all runs
 * @brief GetSpiceDataRawCountsFromMD::exportIndividualDetCounts
 * @param datamdws
 * @param monitormdws
 * @param detid
 * @param vecX
 * @param vecY
 * @param xlabel
 * @param ylabel
 * @param donormalize
 */
void GetSpiceDataRawCountsFromMD::exportIndividualDetCounts(
    API::IMDEventWorkspace_const_sptr datamdws,
    API::IMDEventWorkspace_const_sptr monitormdws, const int detid,
    std::vector<double> &vecX, std::vector<double> &vecY, std::string &xlabel,
    std::string &ylabel, const bool &donormalize) {
  // Get detector counts
  std::vector<double> vecSampleLog;
  std::vector<double> vecDetCounts;
  int runnumber = -1;
  bool get2theta = false;
  if (xlabel.size() == 0) {
    // xlabel is in default and thus use 2-theta for X
    get2theta = true;
  }
  getDetCounts(datamdws, runnumber, detid, vecSampleLog, vecDetCounts,
               get2theta);
  if (!get2theta) {
    getSampleLogValues(datamdws, xlabel, runnumber, vecSampleLog);
  }
  if (vecSampleLog.size() != vecDetCounts.size())
    throw std::runtime_error("Logic error! number of sample logs should be "
                             "same as number of detector's counts.");

  // Get monitor counts
  std::vector<double> vec2thetaMon;
  std::vector<double> vecMonitorCounts;
  if (donormalize)
    getDetCounts(monitormdws, runnumber, detid, vec2thetaMon, vecMonitorCounts,
                 false);

  // FIXME - Consider refactoring in future
  // Normalize if required
  if (donormalize) {
    // check
    if (vecDetCounts.size() != vecMonitorCounts.size())
      throw std::runtime_error(
          "Number of detectors' counts' is different from that of "
          "monitor counts.");

    for (size_t i = 0; i < vecDetCounts.size(); ++i)
      if (vecMonitorCounts[i] > 1.0E-9)
        vecDetCounts[i] = vecDetCounts[i] / vecMonitorCounts[i];
      else
        vecDetCounts[i] = 0.;
  }
  // Sort and output
  const size_t numpts = vecDetCounts.size();
  std::vector<std::pair<double, double>> vecPair(numpts);
  for (size_t i = 0; i < numpts; ++i) {
    vecPair[i] = std::make_pair(vecSampleLog[i], vecDetCounts[i]);
  std::sort(vecPair.begin(), vecPair.end());
  // Apply to output
  vecX.resize(numpts);
  vecY.resize(numpts);
  for (size_t i = 0; i < numpts; ++i) {
    vecX[i] = vecPair[i].first;
    vecY[i] = vecPair[i].second;
  // Set up label
  if (get2theta)
    xlabel = "Degrees";
  if (donormalize)
    ylabel = "Noramlized Intensity";
  else
    ylabel = "Counts";
}

//----------------------------------------------------------------------------------------------
/** Export sample log values accross all runs
 * @brief GetSpiceDataRawCountsFromMD::exportSampleLogValue
 * @param datamdws
 * @param samplelogname :: name of the sample log to be exported
 * @param vecX :: output x values
 * @param vecY :: output y values
 * @param xlabel :: label of x-axis.  It is the name of another sample log.
 * @param ylabel
 */
void GetSpiceDataRawCountsFromMD::exportSampleLogValue(
    API::IMDEventWorkspace_const_sptr datamdws,
    const std::string &samplelogname, std::vector<double> &vecX,
    std::vector<double> &vecY, std::string &xlabel, std::string &ylabel) {
  // prepare
  vecX.clear();
  vecY.clear();

  // Y values
  int runnumber = -1;
  getSampleLogValues(datamdws, samplelogname, runnumber, vecY);
  ylabel = samplelogname;

  // X values
  if (xlabel.size() == 0) {
    // default
    xlabel = "Pt.";
  }
  getSampleLogValues(datamdws, xlabel, runnumber, vecX);

  if (vecX.size() != vecY.size())
    throw std::runtime_error(
        "It is not correct to have two different sizes vectors.");

  // Sort
  const size_t numpts = vecX.size();
  std::vector<std::pair<double, double>> vecPair(numpts);
  for (size_t i = 0; i < numpts; ++i) {
    vecPair[i] = std::make_pair(vecX[i], vecY[i]);
  }
  std::sort(vecPair.begin(), vecPair.end());

  // Apply to output
  vecX.resize(numpts);
  vecY.resize(numpts);
  for (size_t i = 0; i < numpts; ++i) {
    vecX[i] = vecPair[i].first;
    vecY[i] = vecPair[i].second;
  }

  return;
}

//----------------------------------------------------------------------------------------------
/** Get detectors' counts
 * @brief GetSpiceDataRawCountsFromMD::getDetCounts
 * @param mdws
 * @param runnumber :: run number of the detectors having for exporting; -1 for
 * all run numbers
 * @param detid :: detector ID for the detectors for exporting; -1 for all
 * detectors
 * @param vecX :: x-values as 2theta position of detectors to be exported;
 * @param vecY :: raw detector's counts
 * @param formX :: flag to set up vecX
 */
void GetSpiceDataRawCountsFromMD::getDetCounts(
    API::IMDEventWorkspace_const_sptr mdws, const int &runnumber,
    const int &detid, std::vector<double> &vecX, std::vector<double> &vecY,
    bool formX) {
  // Get sample and source position
  if (mdws->getNumExperimentInfo() == 0)
    throw std::runtime_error(
        "There is no ExperimentInfo object that has been set to "
        "input MDEventWorkspace!");

  V3D samplepos;
  V3D sourcepos;

  if (formX) {
    ExperimentInfo_const_sptr expinfo = mdws->getExperimentInfo(0);
    Geometry::IComponent_const_sptr sample =
        expinfo->getInstrument()->getSample();
    samplepos = sample->getPos();
    g_log.debug() << "Sample position is " << samplepos.X() << ", "
                  << samplepos.Y() << ", " << samplepos.Z() << "\n";

    Geometry::IComponent_const_sptr source =
        expinfo->getInstrument()->getSource();
    sourcepos = source->getPos();
    g_log.debug() << "Source position is " << sourcepos.X() << ","
                  << sourcepos.Y() << ", " << sourcepos.Z() << "\n";
    vecX.clear();
  }
  vecY.clear();

  // Go through all events to find out their positions
  IMDIterator *mditer = mdws->createIterator();

  bool scancell = true;
  size_t nextindex = 1;
  while (scancell) {
    // get the number of events of this cell
    size_t numev2 = mditer->getNumEvents();
    g_log.debug() << "MDWorkspace " << mdws->name() << " Cell " << nextindex - 1
                  << ": Number of events = " << numev2
                  << " Does NEXT cell exist = " << mditer->next() << "\n";

    // loop over all the events in current cell
    for (size_t iev = 0; iev < numev2; ++iev) {
      // filter out the events with uninterrested run numbers and detid
      // runnumber/detid < 0 indicates that all run number or all detectors will
      // be taken
      int thisrunnumber = mditer->getInnerRunIndex(iev);
      if (runnumber >= 0 && thisrunnumber != runnumber)
        continue;

      int thisdetid = mditer->getInnerDetectorID(iev);
      if (detid >= 0 && thisdetid != detid)
        continue;

      // get detector position for 2theta
      if (formX) {
        double tempx = mditer->getInnerPosition(iev, 0);
        double tempy = mditer->getInnerPosition(iev, 1);
        double tempz = mditer->getInnerPosition(iev, 2);
        Kernel::V3D detpos(tempx, tempy, tempz);
        Kernel::V3D v_det_sample = detpos - samplepos;
        Kernel::V3D v_sample_src = samplepos - sourcepos;
        double twotheta = v_det_sample.angle(v_sample_src) / M_PI * 180.;
        vecX.push_back(twotheta);
      }

      // add new value to vecPair
      double signal = mditer->getInnerSignal(iev);
      vecY.push_back(signal);
    } // ENDFOR (iev)

    // Advance to next cell
    if (mditer->next()) {
      // advance to next cell
      mditer->jumpTo(nextindex);
      ++nextindex;
    } else {
      // break the loop
      scancell = false;
    }
  } // ENDOF(while)

  delete (mditer);

  return;
}

//----------------------------------------------------------------------------------------------
/** Get sample log values
 * @brief GetSpiceDataRawCountsFromMD::getSampleLogValues
 * @param mdws
 * @param samplelogname
 * @param runnumber
 * @param vecSampleLog
 */
void GetSpiceDataRawCountsFromMD::getSampleLogValues(
    IMDEventWorkspace_const_sptr mdws, const std::string &samplelogname,
    const int runnumber, std::vector<double> &vecSampleLog) {
  // Clear input
  vecSampleLog.clear();

  // Loop over all experiment info objects
  uint16_t numexpinfo = mdws->getNumExperimentInfo();
  for (uint16_t iexp = 0; iexp < numexpinfo; ++iexp) {
    // Get handler to experiment info object
    ExperimentInfo_const_sptr expinfo = mdws->getExperimentInfo(iexp);
    // Skip invalid run
    int thisrunnumber = expinfo->getRunNumber();
    if (thisrunnumber < 0)
      continue;
    // Skip unmatched run: input runnumber < 0 means that all run will be
    // accepted
    if (runnumber >= 0 && thisrunnumber != runnumber)
      continue;
    // Check property exists
    if (!expinfo->run().hasProperty(samplelogname)) {
      std::stringstream ess;
      ess << "Workspace " << mdws->name() << "'s " << iexp
          << "-th ExperimentInfo with "
             "run number " << thisrunnumber
          << " does not have specified property " << samplelogname;
      throw std::runtime_error(ess.str());
    }
    // Get experiment value
    double logvalue = expinfo->run().getPropertyAsSingleValue(samplelogname);
    vecSampleLog.push_back(logvalue);
    g_log.debug() << "Add sample log (" << samplelogname << ") " << logvalue
                  << " of " << iexp << "-th ExperimentInfo "
                  << "\n";
  }

  return;
}

//----------------------------------------------------------------------------------------------
/** Create output workspace
 * @brief GetSpiceDataRawCountsFromMD::createOutputWorkspace
 * @param vecX
 * @param vecY
 * @param xlabel :: only 'Degrees' can be applied to x-axis
 * @param ylabel
 * @return
 */
MatrixWorkspace_sptr GetSpiceDataRawCountsFromMD::createOutputWorkspace(
    const std::vector<double> &vecX, const std::vector<double> &vecY,
    const std::string &xlabel, const std::string &ylabel) {
  // Create MatrixWorkspace
  size_t sizex = vecX.size();
  size_t sizey = vecY.size();
  if (sizex != sizey || sizex == 0)
    throw std::runtime_error("Unable to create output matrix workspace.");

  MatrixWorkspace_sptr outws = boost::dynamic_pointer_cast<MatrixWorkspace>(
      WorkspaceFactory::Instance().create("Workspace2D", 1, sizex, sizey));
  if (!outws)
    throw std::runtime_error("Failed to create output matrix workspace.");

  // Set data
  MantidVec &dataX = outws->dataX(0);
  MantidVec &dataY = outws->dataY(0);
  MantidVec &dataE = outws->dataE(0);
  for (size_t i = 0; i < sizex; ++i) {
    dataX[i] = vecX[i];
    dataY[i] = vecY[i];
    if (dataY[i] > 1.)
      dataE[i] = sqrt(dataY[i]);
    else
      dataE[i] = 1.;
  }

  // Set label
  outws->setYUnitLabel(ylabel);
  if (xlabel.size() != 0) {
    try {
      outws->getAxis(0)->setUnit(xlabel);
    } catch (...) {
      g_log.information() << "Label " << xlabel << " for X-axis is not a unit "
                                                   "registered."
                          << "\n";
    }
  }

} // namespace MDAlgorithms