Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
AppendSpectra.cpp 6.49 KiB
#include "MantidAlgorithms/AppendSpectra.h"
#include "MantidAPI/CommonBinsValidator.h"
#include "MantidAPI/NumericAxis.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/TextAxis.h"
#include "MantidAPI/WorkspaceOpOverloads.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/SingletonHolder.h"
#include "MantidIndexing/IndexInfo.h"

using namespace Mantid::Indexing;
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::DataObjects;

namespace Mantid {
namespace Algorithms {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(AppendSpectra)

/// Algorithm's name for identification. @see Algorithm::name
const std::string AppendSpectra::name() const { return "AppendSpectra"; }

/// Algorithm's version for identification. @see Algorithm::version
int AppendSpectra::version() const { return 1; }

/** Initialize the algorithm's properties.
 */
void AppendSpectra::init() {
  declareProperty(make_unique<WorkspaceProperty<>>(
                      "InputWorkspace1", "", Direction::Input,
                      boost::make_shared<CommonBinsValidator>()),
                  "The name of the first input workspace");
  declareProperty(make_unique<WorkspaceProperty<>>(
                      "InputWorkspace2", "", Direction::Input,
                      boost::make_shared<CommonBinsValidator>()),
                  "The name of the second input workspace");

  declareProperty(
      "ValidateInputs", true,
      "Perform a set of checks that the two input workspaces are compatible.");

  declareProperty("Number", 1,
                  boost::make_shared<BoundedValidator<int>>(1, EMPTY_INT()),
                  "Append the spectra from InputWorkspace2 multiple times (for "
                  "MatrixWorkspaces only)");

  declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
                                                   Direction::Output),
                  "The name of the output workspace");

  declareProperty("MergeLogs", false,
                  "Whether to combine the logs of the two input workspaces");
}

/** Execute the algorithm.
 */
void AppendSpectra::exec() {
  // Retrieve the input workspaces
  MatrixWorkspace_const_sptr ws1 = getProperty("InputWorkspace1");
  MatrixWorkspace_const_sptr ws2 = getProperty("InputWorkspace2");
  event_ws1 = boost::dynamic_pointer_cast<const EventWorkspace>(ws1);
  event_ws2 = boost::dynamic_pointer_cast<const EventWorkspace>(ws2);

  // Make sure that we are not mis-matching EventWorkspaces and other types of
  // workspaces
  if (((event_ws1) && (!event_ws2)) || ((!event_ws1) && (event_ws2))) {
    const std::string message("Only one of the input workspaces are of type "
                              "EventWorkspace; please use matching workspace "
                              "types (both EventWorkspace's or both "
                              "Workspace2D's).");
    g_log.error(message);
    throw std::invalid_argument(message);
  }

  bool ValidateInputs = this->getProperty("ValidateInputs");
  if (ValidateInputs) {
    // Check that the input workspaces meet the requirements for this algorithm
    this->validateInputs(*ws1, *ws2, true);
  }

  const bool mergeLogs = getProperty("MergeLogs");
  const int number = getProperty("Number");

  if (event_ws1 && event_ws2) {
    // Both are event workspaces. Use the special method
    MatrixWorkspace_sptr output = this->execEvent();
    if (number > 1)
      g_log.warning("Number property is ignored for event workspaces");
    if (mergeLogs)
      combineLogs(ws1->run(), ws2->run(), output->mutableRun());
    // Set the output workspace
    setProperty("OutputWorkspace", output);
    return;
  }
  // So it is a workspace 2D.

  // The only restriction, even with ValidateInputs=false
  if (ws1->blocksize() != ws2->blocksize())
    throw std::runtime_error(
        "Workspace2D's must have the same number of bins.");

  MatrixWorkspace_sptr output = execWS2D(*ws1, *ws2);
  for (int i = 1; i < number; i++)
    output = execWS2D(*output, *ws2);
  if (mergeLogs)
    combineLogs(ws1->run(), ws2->run(), output->mutableRun());

  // Set the output workspace
  setProperty("OutputWorkspace",
              boost::dynamic_pointer_cast<MatrixWorkspace>(output));
}

/** If there is an overlap in spectrum numbers between ws1 and ws2,
 * then the spectrum numbers are reset as a simple 1-1 correspondence
 * with the workspace index.
 *
 * @param ws1 The first workspace supplied to the algorithm.
 * @param ws2 The second workspace supplied to the algorithm.
 * @param output The workspace that is going to be returned by the algorithm.
 */
void AppendSpectra::fixSpectrumNumbers(const MatrixWorkspace &ws1,
                                       const MatrixWorkspace &ws2,
                                       MatrixWorkspace &output) {
  specnum_t ws1min;
  specnum_t ws1max;
  getMinMax(ws1, ws1min, ws1max);

  specnum_t ws2min;
  specnum_t ws2max;
  getMinMax(ws2, ws2min, ws2max);

  // is everything possibly ok?
  if (ws2min > ws1max)
    return;

  auto indexInfo = output.indexInfo();
  indexInfo.setSpectrumNumbers(
      0, static_cast<int32_t>(output.getNumberHistograms() - 1));
  output.setIndexInfo(indexInfo);

  const int yAxisNum = 1;
  const auto yAxisWS1 = ws1.getAxis(yAxisNum);
  const auto yAxisWS2 = ws2.getAxis(yAxisNum);
  auto outputYAxis = output.getAxis(yAxisNum);
  const auto ws1len = ws1.getNumberHistograms();

  const bool isTextAxis = yAxisWS1->isText() && yAxisWS2->isText();
  const bool isNumericAxis = yAxisWS1->isNumeric() && yAxisWS2->isNumeric();

  auto outputTextAxis = dynamic_cast<TextAxis *>(outputYAxis);
  for (size_t i = 0; i < output.getNumberHistograms(); ++i) {
    if (isTextAxis) {
      // check if we're outside the spectra of the first workspace
      const std::string inputLabel =
          i < ws1len ? yAxisWS1->label(i) : yAxisWS2->label(i - ws1len);
      outputTextAxis->setLabel(i, !inputLabel.empty() ? inputLabel : "");

    } else if (isNumericAxis) {
      // check if we're outside the spectra of the first workspace
      const double inputVal =
          i < ws1len ? yAxisWS1->getValue(i) : yAxisWS2->getValue(i - ws1len);
      outputYAxis->setValue(i, inputVal);
    }
  }
}

void AppendSpectra::combineLogs(const API::Run &lhs, const API::Run &rhs,
                                API::Run &ans) {
  // No need to worry about ordering here as for Plus - they have to be
  // different workspaces
  if (&lhs != &rhs) {
    ans = lhs;
    ans += rhs;
  }
}

} // namespace Mantid
} // namespace Algorithms