Skip to content
Snippets Groups Projects
CorrectKiKf.cpp 10.7 KiB
Newer Older
#include "MantidAlgorithms/CorrectKiKf.h"
#include "MantidAPI/SpectrumInfo.h"
#include "MantidAPI/WorkspaceUnitValidator.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidGeometry/IDetector.h"
#include "MantidGeometry/Instrument/ParameterMap.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/UnitFactory.h"
namespace Mantid {
namespace Algorithms {

// Register with the algorithm factory
DECLARE_ALGORITHM(CorrectKiKf)

using namespace Kernel;
using namespace API;
using namespace DataObjects;
Peterson, Peter's avatar
Peterson, Peter committed
using std::size_t;

/// Initialisation method
void CorrectKiKf::init() {
  auto wsValidator = boost::make_shared<WorkspaceUnitValidator>("DeltaE");
  this->declareProperty(
      make_unique<WorkspaceProperty<API::MatrixWorkspace>>(
          "InputWorkspace", "", Direction::Input, wsValidator),
      "Name of the input workspace");
  this->declareProperty(
      make_unique<WorkspaceProperty<API::MatrixWorkspace>>(
          "OutputWorkspace", "", Direction::Output),
      "Name of the output workspace, can be the same as the input");
  std::vector<std::string> propOptions{"Direct", "Indirect"};
  this->declareProperty("EMode", "Direct",
                        boost::make_shared<StringListValidator>(propOptions),
                        "The energy mode (default: Direct)");
  auto mustBePositive = boost::make_shared<BoundedValidator<double>>();
  mustBePositive->setLower(0.0);
  this->declareProperty("EFixed", EMPTY_DBL(), mustBePositive,
                        "Value of fixed energy in meV : EI (EMode=Direct) or "
                        "EF (EMode=Indirect) .");
void CorrectKiKf::exec() {
  // Get the workspaces
  MatrixWorkspace_const_sptr inputWS = this->getProperty("InputWorkspace");
  MatrixWorkspace_sptr outputWS = this->getProperty("OutputWorkspace");
  // Check if it is an event workspace
  EventWorkspace_const_sptr eventW =
      boost::dynamic_pointer_cast<const EventWorkspace>(inputWS);
  if (eventW != nullptr) {
    this->execEvent();
  // If input and output workspaces are not the same, create a new workspace for
  // the output
  if (outputWS != inputWS) {
    outputWS = API::WorkspaceFactory::Instance().create(inputWS);
  const size_t size = inputWS->blocksize();
  // Calculate the number of spectra in this workspace
  const int numberOfSpectra = static_cast<int>(inputWS->size() / size);
  API::Progress prog(this, 0.0, 1.0, numberOfSpectra);
  bool negativeEnergyWarning = false;
  const std::string emodeStr = getProperty("EMode");
  double efixedProp = getProperty("EFixed");
  if (efixedProp == EMPTY_DBL()) {
    if (emodeStr == "Direct") {
      // Check if it has been store on the run object for this workspace
      if (inputWS->run().hasProperty("Ei")) {
        Kernel::Property *eiprop = inputWS->run().getProperty("Ei");
        efixedProp = boost::lexical_cast<double>(eiprop->value());
        g_log.debug() << "Using stored Ei value " << efixedProp << "\n";
      } else {
        throw std::invalid_argument(
            "No Ei value has been set or stored within the run information.");
    } else {
      // If not specified, will try to get Ef from the parameter file for
      // indirect geometry,
      // but it will be done for each spectrum separately, in case of different
      // analyzer crystals
  // Get the parameter map
  const ParameterMap &pmap = outputWS->constInstrumentParameters();
  const auto &spectrumInfo = inputWS->spectrumInfo();
  PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
  for (int64_t i = 0; i < int64_t(numberOfSpectra); ++i) {
    PARALLEL_START_INTERUPT_REGION
    double Efi = 0;
    // Now get the detector object for this histogram to check if monitor
    // or to get Ef for indirect geometry
    if (emodeStr == "Indirect") {
      if (efixedProp != EMPTY_DBL())
        Efi = efixedProp;
Ian Bush's avatar
Ian Bush committed
      // If a DetectorGroup is present should provide a value as a property
      // instead
      else if (spectrumInfo.hasUniqueDetector(i)) {
        getEfixedFromParameterMap(Efi, i, spectrumInfo, pmap);
Ian Bush's avatar
Ian Bush committed
      } else {
        g_log.information() << "Workspace Index " << i
                            << ": cannot find detector"
                            << "\n";
    auto &yOut = outputWS->mutableY(i);
    auto &eOut = outputWS->mutableE(i);
    const auto &xIn = inputWS->points(i);
    auto &yIn = inputWS->y(i);
    auto &eIn = inputWS->e(i);
    // Copy the energy transfer axis
    outputWS->setSharedX(i, inputWS->sharedX(i));
    for (unsigned int j = 0; j < size; ++j) {
      double Ei = 0.;
      double Ef = 0.;
      if (emodeStr == "Direct") // Ei=Efixed
      } else // Ef=Efixed
      {
      }
      // if Ei or Ef is negative, it should be a warning
      // however, if the intensity is 0 (histogram goes to energy transfer
      // higher than Ei) it is still ok, so no warning.
      if ((Ei <= 0) || (Ef <= 0)) {
        kioverkf = 0.;
        if (yIn[j] != 0)
          negativeEnergyWarning = true;
      } else
        kioverkf = std::sqrt(Ei / Ef);

      yOut[j] = yIn[j] * kioverkf;
      eOut[j] = eIn[j] * kioverkf;
    }
    prog.report();
    PARALLEL_END_INTERUPT_REGION
  } // end for i
  PARALLEL_CHECK_INTERUPT_REGION
  if (negativeEnergyWarning)
Hahn, Steven's avatar
Hahn, Steven committed
    g_log.information() << "Ef <= 0 or Ei <= 0 in at least one spectrum!!!!\n";
  if ((negativeEnergyWarning) && (efixedProp == EMPTY_DBL()))
    g_log.information() << "Try to set fixed energy\n";
  this->setProperty("OutputWorkspace", outputWS);
}

/**
 * Execute CorrectKiKf for event workspaces
 *
 */

void CorrectKiKf::execEvent() {
  g_log.information("Processing event workspace");

  const MatrixWorkspace_const_sptr matrixInputWS =
      getProperty("InputWorkspace");
  auto inputWS =
      boost::dynamic_pointer_cast<const EventWorkspace>(matrixInputWS);
  API::MatrixWorkspace_sptr matrixOutputWS = getProperty("OutputWorkspace");
  if (matrixOutputWS != matrixInputWS) {
    matrixOutputWS = matrixInputWS->clone();
    setProperty("OutputWorkspace", matrixOutputWS);
  auto outputWS = boost::dynamic_pointer_cast<EventWorkspace>(matrixOutputWS);

  const std::string emodeStr = getProperty("EMode");
  double efixedProp = getProperty("EFixed"), efixed;
  if (efixedProp == EMPTY_DBL()) {
    if (emodeStr == "Direct") {
      // Check if it has been store on the run object for this workspace
      if (inputWS->run().hasProperty("Ei")) {
        Kernel::Property *eiprop = inputWS->run().getProperty("Ei");
        efixedProp = boost::lexical_cast<double>(eiprop->value());
        g_log.debug() << "Using stored Ei value " << efixedProp << "\n";
      } else {
        throw std::invalid_argument(
            "No Ei value has been set or stored within the run information.");
    } else {
      // If not specified, will try to get Ef from the parameter file for
      // indirect geometry,
      // but it will be done for each spectrum separately, in case of different
      // analyzer crystals
  // Get the parameter map
  const ParameterMap &pmap = outputWS->constInstrumentParameters();
  int64_t numHistograms = static_cast<int64_t>(inputWS->getNumberHistograms());
  const auto &spectrumInfo = inputWS->spectrumInfo();
  API::Progress prog = API::Progress(this, 0.0, 1.0, numHistograms);
  PARALLEL_FOR_IF(Kernel::threadSafe(*outputWS))
  for (int64_t i = 0; i < numHistograms; ++i) {
    double Efi = 0;
    // Now get the detector object for this histogram to check if monitor
    // or to get Ef for indirect geometry
    if (emodeStr == "Indirect") {
Ian Bush's avatar
Ian Bush committed
      if (efixedProp != EMPTY_DBL()) {
        Efi = efixedProp;
Ian Bush's avatar
Ian Bush committed
        // If a DetectorGroup is present should provide a value as a property
        // instead
      } else if (spectrumInfo.hasUniqueDetector(i)) {
        getEfixedFromParameterMap(Efi, i, spectrumInfo, pmap);
Ian Bush's avatar
Ian Bush committed
      } else {
        g_log.information() << "Workspace Index " << i
                            << ": cannot find detector"
                            << "\n";
      }
    if (emodeStr == "Indirect")
      efixed = Efi;
    else
      efixed = efixedProp;

    // Do the correction
    auto &evlist = outputWS->getSpectrum(i);
    switch (evlist.getEventType()) {
    case TOF:
      // Switch to weights if needed.
      evlist.switchTo(WEIGHTED);
    /* no break */
    // Fall through

    case WEIGHTED:
      correctKiKfEventHelper(evlist.getWeightedEvents(), efixed, emodeStr);
      break;

    case WEIGHTED_NOTIME:
      correctKiKfEventHelper(evlist.getWeightedEventsNoTime(), efixed,
                             emodeStr);
      break;
    }

    prog.report();
    PARALLEL_END_INTERUPT_REGION
  }
  PARALLEL_CHECK_INTERUPT_REGION

  outputWS->clearMRU();
  if (inputWS->getNumberEvents() != outputWS->getNumberEvents()) {
    g_log.information() << "Ef <= 0 or Ei <= 0 for "
                        << inputWS->getNumberEvents() -
Moore's avatar
Moore committed
                               outputWS->getNumberEvents() << " events, out of "
                        << inputWS->getNumberEvents() << '\n';
    if (efixedProp == EMPTY_DBL())
      g_log.information() << "Try to set fixed energy\n";
template <class T>
void CorrectKiKf::correctKiKfEventHelper(std::vector<T> &wevector,
                                         double efixed,
                                         const std::string emodeStr) {
  double Ei, Ef;
  float kioverkf;
  typename std::vector<T>::iterator it;
  for (it = wevector.begin(); it < wevector.end();) {
    if (emodeStr == "Direct") // Ei=Efixed
Ian Bush's avatar
Ian Bush committed
    } else // Ef=Efixed
    // if Ei or Ef is negative, delete the event
    if ((Ei <= 0) || (Ef <= 0)) {
      it = wevector.erase(it);
    } else {
      kioverkf = static_cast<float>(std::sqrt(Ei / Ef));
      it->m_weight *= kioverkf;
      it->m_errorSquared *= kioverkf * kioverkf;
void CorrectKiKf::getEfixedFromParameterMap(double &Efi, int64_t i,
                                            const SpectrumInfo &spectrumInfo,
                                            const ParameterMap &pmap) {
Ian Bush's avatar
Ian Bush committed
  Efi = 0;

  if (spectrumInfo.isMonitor(i))
    return;

  const auto &det = spectrumInfo.detector(i);
  Parameter_sptr par = pmap.getRecursive(&det, "Efixed");
  if (par) {
    Efi = par->value<double>();
    g_log.debug() << "Detector: " << det.getID() << " EFixed: " << Efi << "\n";
  }
}

} // namespace Algorithm
} // namespace Mantid