Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
DiffractionFocussing.cpp 8.98 KiB
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/DiffractionFocussing.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidKernel/Unit.h"

#include <fstream>
#include <limits>
#include <map>

namespace Mantid {
namespace Algorithms {

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

/// Constructor
DiffractionFocussing::DiffractionFocussing()
    : API::Algorithm(), API::DeprecatedAlgorithm() {
  this->useAlgorithm("DiffractionFocussing", 2);
}

using namespace Kernel;
using API::WorkspaceProperty;
using API::MatrixWorkspace_sptr;
using API::MatrixWorkspace;
using API::FileProperty;

/** Initialisation method. Declares properties to be used in algorithm.
 *
 */
void DiffractionFocussing::init() {
  declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
                      "InputWorkspace", "", Direction::Input),
                  "The input workspace");
  declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
                      "OutputWorkspace", "", Direction::Output),
                  "The result of diffraction focussing of InputWorkspace");
  declareProperty(make_unique<FileProperty>("GroupingFileName", "",
                                            FileProperty::Load, ".cal"),
                  "The name of the CalFile with grouping data");
}

/** Executes the algorithm
 *
 *  @throw Exception::FileError If the grouping file cannot be opened or read
 *successfully
 *  @throw runtime_error If unable to run one of the Child Algorithms
 *successfully
 */
void DiffractionFocussing::exec() {
  // retrieve the properties
  std::string groupingFileName = getProperty("GroupingFileName");

  // Get the input workspace
  MatrixWorkspace_sptr inputW = getProperty("InputWorkspace");

  bool dist = inputW->isDistribution();

  // do this first to check that a valid file is available before doing any work
  auto detectorGroups = readGroupingFile(groupingFileName); // <group, UDET>

  // Convert to d-spacing units
  API::MatrixWorkspace_sptr tmpW = convertUnitsToDSpacing(inputW);

  // Rebin to a common set of bins
  RebinWorkspace(tmpW);

  std::set<int64_t> groupNumbers;
  for (std::multimap<int64_t, int64_t>::const_iterator d =
           detectorGroups.begin();
       d != detectorGroups.end(); ++d) {
    if (groupNumbers.find(d->first) == groupNumbers.end()) {
      groupNumbers.insert(d->first);
    }
  }

  int iprogress = 0;
  int iprogress_count = static_cast<int>(groupNumbers.size());
  int iprogress_step = iprogress_count / 100;
  if (iprogress_step == 0)
    iprogress_step = 1;
  std::vector<int64_t> resultIndeces;
  for (auto g = groupNumbers.cbegin(); g != groupNumbers.end(); ++g) {
    if (iprogress++ % iprogress_step == 0) {
      progress(0.68 + double(iprogress) / iprogress_count / 3);
    }
    auto from = detectorGroups.lower_bound(*g);
    auto to = detectorGroups.upper_bound(*g);
    std::vector<detid_t> detectorList;
    for (auto d = from; d != to; ++d)
      detectorList.push_back(static_cast<detid_t>(d->second));
    // Want version 1 of GroupDetectors here
    API::IAlgorithm_sptr childAlg =
        createChildAlgorithm("GroupDetectors", -1.0, -1.0, true, 1);
    childAlg->setProperty("Workspace", tmpW);
    childAlg->setProperty<std::vector<detid_t>>("DetectorList", detectorList);
    childAlg->executeAsChildAlg();
    try {
      // get the index of the combined spectrum
      int ri = childAlg->getProperty("ResultIndex");
      if (ri >= 0) {
        resultIndeces.push_back(ri);
      }
    } catch (...) {
      throw std::runtime_error(
          "Unable to get Properties from GroupDetectors Child Algorithm");
    }
  }

  // Discard left-over spectra, but print warning message giving number
  // discarded
  int discarded = 0;
  const int64_t oldHistNumber = tmpW->getNumberHistograms();
  API::Axis *spectraAxis = tmpW->getAxis(1);
  for (int64_t i = 0; i < oldHistNumber; i++)
    if (spectraAxis->spectraNo(i) >= 0 &&
        find(resultIndeces.begin(), resultIndeces.end(), i) ==
            resultIndeces.end()) {
      ++discarded;
    }
  g_log.warning() << "Discarded " << discarded
                  << " spectra that were not assigned to any group\n";

  // Running GroupDetectors leads to a load of redundant spectra
  // Create a new workspace that's the right size for the meaningful spectra and
  // copy them in
  int64_t newSize = tmpW->blocksize();
  API::MatrixWorkspace_sptr outputW = API::WorkspaceFactory::Instance().create(
      tmpW, resultIndeces.size(), newSize + 1, newSize);

  for (int64_t hist = 0; hist < static_cast<int64_t>(resultIndeces.size());
       hist++) {
    int64_t i = resultIndeces[hist];
    outputW->setHistogram(hist, tmpW->histogram(i));
    auto &inSpec = tmpW->getSpectrum(i);
    outputW->getSpectrum(hist).setSpectrumNo(inSpec.getSpectrumNo());
    inSpec.setSpectrumNo(-1);
  }

  progress(1.);

  outputW->setDistribution(dist);

  // Assign it to the output workspace property
  setProperty("OutputWorkspace", outputW);
}

/// Run ConvertUnits as a Child Algorithm to convert to dSpacing
MatrixWorkspace_sptr DiffractionFocussing::convertUnitsToDSpacing(
    const API::MatrixWorkspace_sptr &workspace) {
  const std::string CONVERSION_UNIT = "dSpacing";

  Unit_const_sptr xUnit = workspace->getAxis(0)->unit();

  g_log.information() << "Converting units from " << xUnit->label().ascii()
                      << " to " << CONVERSION_UNIT << ".\n";

  API::IAlgorithm_sptr childAlg =
      createChildAlgorithm("ConvertUnits", 0.34, 0.66);
  childAlg->setProperty("InputWorkspace", workspace);
  childAlg->setPropertyValue("Target", CONVERSION_UNIT);
  childAlg->executeAsChildAlg();

  return childAlg->getProperty("OutputWorkspace");
}

/// Run Rebin as a Child Algorithm to harmonise the bin boundaries
void DiffractionFocussing::RebinWorkspace(
    API::MatrixWorkspace_sptr &workspace) {

  double min = 0;
  double max = 0;
  double step = 0;

  calculateRebinParams(workspace, min, max, step);
  std::vector<double> paramArray{min, -step, max};

  g_log.information() << "Rebinning from " << min << " to " << max << " in "
                      << step << " logaritmic steps.\n";

  API::IAlgorithm_sptr childAlg = createChildAlgorithm("Rebin");
  childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", workspace);
  childAlg->setProperty<std::vector<double>>("Params", paramArray);
  childAlg->executeAsChildAlg();
  workspace = childAlg->getProperty("OutputWorkspace");
}

/** Calculates rebin parameters: the min and max bin boundaries and the
   logarithmic step. The aim is to have approx.
    the same number of bins as in the input workspace.
    @param workspace :: The workspace being rebinned
    @param min ::       (return) The calculated frame starting point
    @param max ::       (return) The calculated frame ending point
    @param step ::      (return) The calculated bin width
 */
void DiffractionFocussing::calculateRebinParams(
    const API::MatrixWorkspace_const_sptr &workspace, double &min, double &max,
    double &step) {

  min = std::numeric_limits<double>::max();
  // for min and max we need to iterate over the data block and investigate each
  // one
  int64_t length = workspace->getNumberHistograms();
  for (int64_t i = 0; i < length; i++) {
    auto &xVec = workspace->x(i);
    const double &localMin = xVec.front();
    const double &localMax = xVec.back();
    if (localMin != std::numeric_limits<double>::infinity() &&
        localMax != std::numeric_limits<double>::infinity()) {
      min = std::min(min, localMin);
      max = std::max(max, localMax);
    }
  }

  if (min <= 0.)
    min = 1e-6;

  // step is easy
  double n = static_cast<double>(workspace->blocksize());
  step = (log(max) - log(min)) / n;
}

/**
 * Reads in the file with the grouping information
 * @param groupingFileName :: [input] Grouping .cal file name
 * @returns :: map of groups to detector IDs
 * @throws FileError if can't read the file
 */
std::multimap<int64_t, int64_t>
DiffractionFocussing::readGroupingFile(std::string groupingFileName) {
  std::ifstream grFile(groupingFileName.c_str());
  if (!grFile) {
    g_log.error() << "Unable to open grouping file " << groupingFileName
                  << '\n';
    throw Exception::FileError("Error reading .cal file", groupingFileName);
  }

  std::multimap<int64_t, int64_t> detectorGroups;
  std::string str;
  while (getline(grFile, str)) {
    if (str.empty() || str[0] == '#')
      continue;
    std::istringstream istr(str);
    int n, udet, sel, group;
    double offset;
    istr >> n >> udet >> offset >> sel >> group;
    // Check the line wasn't badly formatted - return a failure if it is
    // if ( ! istr.good() ) return false;
    // only allow groups with +ve ids
    if ((sel) && (group > 0)) {
      detectorGroups.emplace(group, udet);
    }
  }
  return detectorGroups;
}

} // namespace Algorithm
} // namespace Mantid