Skip to content
Snippets Groups Projects
CalculateEfficiency.cpp 15.1 KiB
Newer Older
#include "MantidAlgorithms/CalculateEfficiency.h"
#include "MantidAPI/SpectrumInfo.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/EventList.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/ICompAssembly.h"
#include "MantidGeometry/IDTypes.h"
#include "MantidGeometry/Instrument/Detector.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidGeometry/Instrument/RectangularDetector.h"
#include <vector>
namespace Mantid {
namespace Algorithms {

// Register the class into the algorithm factory
DECLARE_ALGORITHM(CalculateEfficiency)

using namespace Kernel;
using namespace API;
using namespace Geometry;
using namespace DataObjects;
void CalculateEfficiency::init() {
      make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
      "The workspace containing the flood data");
      make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
                                       Direction::Output),
      "The name of the workspace to be created as the output of the algorithm");
  auto positiveDouble = boost::make_shared<BoundedValidator<double>>();
  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("MaskedFullComponent", "",
                  "Component Name to fully mask according to the IDF file.");
  declareProperty(
      make_unique<ArrayProperty<int>>("MaskedEdges"),
      "Number of pixels to mask on the edges: X-low, X-high, Y-low, Y-high");
  declareProperty(
      "MaskedComponent", "",
      "Component Name to mask the edges according to the IDF file.");
void CalculateEfficiency::exec() {

  // Minimum efficiency. Pixels with lower efficiency will be masked
  double min_eff = getProperty("MinEfficiency");
  // Maximum efficiency. Pixels with higher efficiency will be masked
  double max_eff = getProperty("MaxEfficiency");

  // Get the input workspace
  MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
  MatrixWorkspace_sptr rebinnedWS; // = inputWS;
  //  BioSANS has 2 detectors and reduces one at the time: one is masked!
  //  We must use that masked detector
  const std::string maskedFullComponent =
      getPropertyValue("MaskedFullComponent");
  if (!maskedFullComponent.empty()) {
    g_log.debug() << "CalculateEfficiency: Masking Full Component: "
                  << maskedFullComponent << "\n";
    maskComponent(*inputWS, maskedFullComponent);

  // BioSANS has 2 detectors and the front masks the back!!!!
  // We must mask the shaded part to calculate efficency
  std::vector<int> maskedEdges = getProperty("MaskedEdges");
  if (!maskedEdges.empty() && (maskedEdges[0] > 0 || maskedEdges[1] > 0 ||
                               maskedEdges[2] > 0 || maskedEdges[3] > 0)) {
    g_log.debug() << "CalculateEfficiency: Masking edges length = "
                  << maskedEdges.size() << ")"
                  << " of the component " << maskedFullComponent << "\n";
    const std::string maskedComponent = getPropertyValue("MaskedComponent");
    maskEdges(inputWS, maskedEdges[0], maskedEdges[1], maskedEdges[2],
              maskedEdges[3], maskedComponent);
  }
  MatrixWorkspace_sptr outputWS; // = getProperty("OutputWorkspace");
  // DataObjects::EventWorkspace_const_sptr inputEventWS =
  // boost::dynamic_pointer_cast<const EventWorkspace>(inputWS);
  // Sum up all the wavelength bins
  IAlgorithm_sptr childAlg = createChildAlgorithm("Integration", 0.0, 0.2);
  childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputWS);
  childAlg->executeAsChildAlg();
  rebinnedWS = childAlg->getProperty("OutputWorkspace");
  outputWS = WorkspaceFactory::Instance().create(rebinnedWS);
  WorkspaceFactory::Instance().initializeFromParent(*inputWS, *outputWS, false);
Hahn, Steven's avatar
Hahn, Steven committed
  for (int i = 0; i < static_cast<int>(rebinnedWS->getNumberHistograms());
       i++) {
    outputWS->setSharedX(i, rebinnedWS->sharedX(i));
  setProperty("OutputWorkspace", outputWS);
  double sum = 0.0;
  double err = 0.0;
  int npixels = 0;

  // Loop over spectra and sum all the counts to get normalization
  // Skip monitors and masked detectors
  sumUnmaskedDetectors(rebinnedWS, sum, err, npixels);

  // Normalize each detector pixel by the sum we just found to get the
  // relative efficiency. If the minimum and maximum efficiencies are
  // provided, the pixels falling outside this range will be marked
  // as 'masked' in both the input and output workspace.
  // We mask detectors in the input workspace so that we can resum the
  // counts to find a new normalization factor that takes into account
  // the newly masked detectors.
  normalizeDetectors(rebinnedWS, outputWS, sum, err, npixels, min_eff, max_eff);

  if (!isEmpty(min_eff) || !isEmpty(max_eff)) {
    // Recompute the normalization, excluding the pixels that were outside
    // the acceptable efficiency range.
    sumUnmaskedDetectors(rebinnedWS, sum, err, npixels);

    // Now that we have a normalization factor that excludes bad pixels,
    // recompute the relative efficiency.
    // We pass EMPTY_DBL() to avoid masking pixels that might end up high or low
    // after the new normalization.
    normalizeDetectors(rebinnedWS, outputWS, sum, err, npixels, EMPTY_DBL(),
                       EMPTY_DBL());
 * @param rebinnedWS: workspace where all the wavelength bins have been grouped
 *together
 * @param sum: sum of all the unmasked detector pixels (counts)
 * @param error: error on sum (counts)
 * @param nPixels: number of unmasked detector pixels that contributed to sum
 */
void CalculateEfficiency::sumUnmaskedDetectors(MatrixWorkspace_sptr rebinnedWS,
                                               double &sum, double &error,
                                               int &nPixels) {
  // Number of spectra
  const int numberOfSpectra =
      static_cast<int>(rebinnedWS->getNumberHistograms());
  sum = 0.0;
  error = 0.0;
  nPixels = 0;

  const auto &spectrumInfo = rebinnedWS->spectrumInfo();
  for (int i = 0; i < numberOfSpectra; i++) {
    progress(0.2 + 0.2 * i / numberOfSpectra, "Computing sensitivity");
    // Skip if we have a monitor or if the detector is masked.
    if (spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i))
      continue;

    // Retrieve the spectrum into a vector
    auto &YValues = rebinnedWS->y(i);
    auto &YErrors = rebinnedWS->e(i);

    sum += YValues[0];
    error += YErrors[0] * YErrors[0];
    nPixels++;
  }
  g_log.debug() << "sumUnmaskedDetectors: unmasked pixels = " << nPixels
                << " from a total of " << numberOfSpectra << "\n";

  error = std::sqrt(error);
}

/*
 * Normalize each detector to produce the relative detector efficiency.
 * Pixels that fall outside those efficiency limits will be masked in both
 * the input and output workspaces.
 *
 * @param rebinnedWS: input workspace
 * @param outputWS: output workspace containing the relative efficiency
 * @param sum: sum of all the unmasked detector pixels (counts)
 * @param error: error on sum (counts)
 * @param nPixels: number of unmasked detector pixels that contributed to sum
 */

void CalculateEfficiency::normalizeDetectors(MatrixWorkspace_sptr rebinnedWS,
                                             MatrixWorkspace_sptr outputWS,
                                             double sum, double error,
                                             int nPixels, double min_eff,
                                             double max_eff) {
  // Number of spectra
  const size_t numberOfSpectra = rebinnedWS->getNumberHistograms();

  // Empty vector to store the pixels that outside the acceptable efficiency
  // range
  std::vector<size_t> dets_to_mask;

  const auto &spectrumInfo = rebinnedWS->spectrumInfo();
  for (size_t i = 0; i < numberOfSpectra; i++) {
    const double currProgress =
Hahn, Steven's avatar
Hahn, Steven committed
        0.4 +
        0.2 * (static_cast<double>(i) / static_cast<double>(numberOfSpectra));
    progress(currProgress, "Computing sensitivity");
    // If this spectrum is masked, skip to the next one
    if (spectrumInfo.isMasked(i))
      continue;

    // Retrieve the spectrum into a vector
    auto &YIn = rebinnedWS->y(i);
    auto &EIn = rebinnedWS->e(i);
    auto &YOut = outputWS->mutableY(i);
    auto &EOut = outputWS->mutableE(i);
    // If this detector is a monitor, skip to the next one
    if (spectrumInfo.isMonitor(i)) {
      YOut[0] = 1.0;
      EOut[0] = 0.0;
      continue;
    // Normalize counts to get relative efficiency
    YOut[0] = nPixels / sum * YIn[0];
    const double err_sum = YIn[0] / sum * error;
    EOut[0] = nPixels / std::abs(sum) *
              std::sqrt(EIn[0] * EIn[0] + err_sum * err_sum);

    // Mask this detector if the signal is outside the acceptable band
    if (!isEmpty(min_eff) && YOut[0] < min_eff)
      dets_to_mask.push_back(i);
    if (!isEmpty(max_eff) && YOut[0] > max_eff)
      dets_to_mask.push_back(i);
  }

  g_log.debug() << "normalizeDetectors: Masked pixels outside the acceptable "
                   "efficiency range [" << min_eff << "," << max_eff
                << "] = " << dets_to_mask.size()
                << " from a total of non masked = " << nPixels
                << " (from a total number of spectra in the ws = "
                << numberOfSpectra << ")\n";

  // If we identified pixels to be masked, mask them now
  if (!dets_to_mask.empty()) {
    // Mask detectors that were found to be outside the acceptable efficiency
    // band
    try {
      IAlgorithm_sptr mask = createChildAlgorithm("MaskDetectors", 0.8, 0.9);
      // First we mask detectors in the output workspace
      mask->setProperty<MatrixWorkspace_sptr>("Workspace", outputWS);
      mask->setProperty<std::vector<size_t>>("WorkspaceIndexList",
                                             dets_to_mask);
      mask->execute();

      mask = createChildAlgorithm("MaskDetectors", 0.9, 1.0);
      // Then we mask the same detectors in the input workspace
      mask->setProperty<MatrixWorkspace_sptr>("Workspace", rebinnedWS);
      mask->setProperty<std::vector<size_t>>("WorkspaceIndexList",
                                             dets_to_mask);
      mask->execute();
    } catch (std::invalid_argument &err) {
      std::stringstream e;
      e << "Invalid argument to MaskDetectors Child Algorithm: " << err.what();
      g_log.error(e.str());
    } catch (std::runtime_error &err) {
      std::stringstream e;
      e << "Unable to successfully run MaskDetectors Child Algorithm: "
        << err.what();
      g_log.error(e.str());
/**
 * Fully masks one component named componentName
 * @param ws :: workspace with the respective instrument assigned
 * @param componentName :: must be a known CompAssembly.
 */
void CalculateEfficiency::maskComponent(MatrixWorkspace &ws,
                                        const std::string &componentName) {
  auto instrument = ws.getInstrument();
  try {
    boost::shared_ptr<const Geometry::ICompAssembly> component =
        boost::dynamic_pointer_cast<const Geometry::ICompAssembly>(
            instrument->getComponentByName(componentName));
    if (!component) {
      g_log.warning("Component " + componentName +
                    " expected to be a CompAssembly, e.g., a bank. Component " +
                    componentName + " not masked!");
      return;
    }
    std::vector<detid_t> detectorList;
    for (int x = 0; x < component->nelements(); x++) {
      boost::shared_ptr<Geometry::ICompAssembly> xColumn =
          boost::dynamic_pointer_cast<Geometry::ICompAssembly>((*component)[x]);
      for (int y = 0; y < xColumn->nelements(); y++) {
        boost::shared_ptr<Geometry::Detector> detector =
            boost::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
        if (detector) {
          auto detID = detector->getID();
          detectorList.push_back(detID);
        }
      }
    }
    auto indexList = ws.getIndicesFromDetectorIDs(detectorList);
    auto &spectrumInfo = ws.mutableSpectrumInfo();
    for (const auto &idx : indexList) {
      ws.getSpectrum(idx).clearData();
      spectrumInfo.setMasked(idx, true);
    }
  } catch (std::exception &) {
    g_log.warning("Expecting the component " + componentName +
                  " to be a CompAssembly, e.g., a bank. Component not masked!");
  }
}

 * Mask edges of a RectangularDetector
 * @param ws :: Input workspace
 * @param left :: number of columns to mask left
 * @param right :: number of columns to mask right
 * @param high :: number of rows to mask top
 * @param low :: number of rows to mask Bottom
 * @param componentName :: Must be a RectangularDetector
 */
void CalculateEfficiency::maskEdges(MatrixWorkspace_sptr ws, int left,
                                    int right, int high, int low,
                                    const std::string &componentName) {

  auto instrument = ws->getInstrument();

  boost::shared_ptr<Mantid::Geometry::RectangularDetector> component;
  try {
    component =
        boost::const_pointer_cast<Mantid::Geometry::RectangularDetector>(
            boost::dynamic_pointer_cast<
                const Mantid::Geometry::RectangularDetector>(
                instrument->getComponentByName(componentName)));
  } catch (std::exception &) {
    g_log.warning("Expecting the component " + componentName +
                  " to be a RectangularDetector. maskEdges not executed.");
    return;
  }
  if (!component) {
    g_log.warning("Component " + componentName +
                  " is not a RectangularDetector. MaskEdges not executed.");
    return;
  }

  std::vector<int> IDs;
  int i = 0;

  while (i < left * component->idstep()) {
    IDs.push_back(component->idstart() + i);
    i += 1;
  }
  // right
  i = component->maxDetectorID() - right * component->idstep();
  while (i < component->maxDetectorID()) {
    IDs.push_back(i);
    i += 1;
  }
  // low: 0,256,512,768,..,1,257,513
  for (int row = 0; row < low; row++) {
    i = row + component->idstart();
    while (i < component->nelements() * component->idstep() -
                   component->idstep() + low + component->idstart()) {
      IDs.push_back(i);
      i += component->idstep();
    }
  }
  // high # 255, 511, 767..
  for (int row = 0; row < high; row++) {
    i = component->idstep() + component->idstart() - row - 1;
    while (i < component->nelements() * component->idstep() +
                   component->idstart()) {
      IDs.push_back(i);
      i += component->idstep();
    }
  }

  g_log.debug() << "CalculateEfficiency::maskEdges Detector Ids to Mask:"
                << std::endl;
  for (auto id : IDs) {
    g_log.debug() << id << " ";
  }
  g_log.debug() << std::endl;

  IAlgorithm_sptr maskAlg = createChildAlgorithm("MaskDetectors");
  maskAlg->setChild(true);
  maskAlg->setProperty("Workspace", ws);
  maskAlg->setProperty("DetectorList", IDs);
  maskAlg->execute();
}

} // namespace Algorithms
} // namespace Mantid