Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ConvertToMDMinMaxLocal.cpp 7.38 KiB
#include "MantidMDAlgorithms/ConvertToMDMinMaxLocal.h"

#include <cfloat>

#include "MantidKernel/ArrayProperty.h"

#include "MantidMDAlgorithms/ConvToMDSelector.h"
#include "MantidMDAlgorithms/MDWSTransform.h"
#include "MantidMDAlgorithms/UnitsConversionHelper.h"

using namespace Mantid::Kernel;
using namespace Mantid::API;

namespace Mantid {
namespace MDAlgorithms {

// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(ConvertToMDMinMaxLocal)

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

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

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

//----------------------------------------------------------------------------------------------
void ConvertToMDMinMaxLocal::init() {
  ConvertToMDParent::init();

  declareProperty(
      new Kernel::ArrayProperty<double>("MinValues", Direction::Output));
  declareProperty(
      new Kernel::ArrayProperty<double>("MaxValues", Direction::Output));
}

//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/

void ConvertToMDMinMaxLocal::exec() {

  // -------- get Input workspace
  Mantid::API::MatrixWorkspace_sptr InWS2D = getProperty("InputWorkspace");

  // Collect and Analyze the requests to the job, specified by the input
  // parameters:
  // a) Q selector:
  std::string QModReq = getProperty("QDimensions");
  // b) the energy exchange mode
  std::string dEModReq = getProperty("dEAnalysisMode");
  // c) other dim property;
  std::vector<std::string> otherDimNames = getProperty("OtherDimensions");
  // d) The output dimensions in the Q3D mode, processed together with
  // QConversionScales
  std::string QFrame = getProperty("Q3DFrames");
  // e) part of the procedure, specifying the target dimensions units. Currently
  // only Q3D target units can be converted to different flavors of hkl
  std::string convertTo_ = getProperty("QConversionScales");

  // Build the target ws description as function of the input & output ws and
  // the parameters, supplied to the algorithm
  MDWSDescription targWSDescr;

  // get raw pointer to Q-transformation (do not delete this pointer, it's held
  // by MDTransfFactory!)
  MDTransfInterface *pQtransf =
      MDTransfFactory::Instance().create(QModReq).get();
  // get number of dimensions this Q transformation generates from the
  // workspace.
  auto iEmode = Kernel::DeltaEMode::fromString(dEModReq);
  // get total number of dimensions the workspace would have.
  unsigned int nMatrixDim = pQtransf->getNMatrixDimensions(iEmode, InWS2D);
  // total number of dimensions
  size_t nDim = nMatrixDim + otherDimNames.size();

  std::vector<double> MinValues, MaxValues;
  MinValues.resize(nDim, -FLT_MAX / 10);
  MaxValues.resize(nDim, FLT_MAX / 10);
  // verify that the number min/max values is equivalent to the number of
  // dimensions defined by properties and min is less max
  targWSDescr.setMinMax(MinValues, MaxValues);
  targWSDescr.buildFromMatrixWS(InWS2D, QModReq, dEModReq, otherDimNames);
  // add runindex to the target workspace description for further usage as the
  // identifier for the events, which come from this run.
  targWSDescr.addProperty("RUN_INDEX", uint16_t(0), true);

  // instantiate class, responsible for defining Mslice-type projection
  MDAlgorithms::MDWSTransform MsliceProj;
  // identify if u,v are present among input parameters and use defaults if not
  std::vector<double> ut = getProperty("UProj");
  std::vector<double> vt = getProperty("VProj");
  std::vector<double> wt = getProperty("WProj");
  try {
    // otherwise input uv are ignored -> later it can be modified to set ub
    // matrix if no given, but this may over-complicate things.
    MsliceProj.setUVvectors(ut, vt, wt);
  } catch (std::invalid_argument &) {
    g_log.error() << "The projections are coplanar. Will use defaults "
                     "[1,0,0],[0,1,0] and [0,0,1]" << std::endl;
  }

  // set up target coordinate system and identify/set the (multi) dimension's
  // names to use
  targWSDescr.m_RotMatrix =
      MsliceProj.getTransfMatrix(targWSDescr, QFrame, convertTo_);

  // preprocess detectors (or make fake detectors in CopyMD case)
  targWSDescr.m_PreprDetTable = this->preprocessDetectorsPositions(
      InWS2D, dEModReq, false, std::string(getProperty("PreprocDetectorsWS")));

  // do the job
  findMinMaxValues(targWSDescr, pQtransf, iEmode, MinValues, MaxValues);

  setProperty("MinValues", MinValues);
  setProperty("MaxValues", MaxValues);
}

void ConvertToMDMinMaxLocal::findMinMaxValues(MDWSDescription &WSDescription,
                                              MDTransfInterface *const pQtransf,
                                              Kernel::DeltaEMode::Type iEMode,
                                              std::vector<double> &MinValues,
                                              std::vector<double> &MaxValues) {

  MDAlgorithms::UnitsConversionHelper unitsConverter;
  double signal(1), errorSq(1);
  //
  size_t nDims = MinValues.size();
  MinValues.assign(nDims, DBL_MAX);
  MaxValues.assign(nDims, -DBL_MAX);
  //
  auto inWS = WSDescription.getInWS();
  std::string convUnitsID = pQtransf->inputUnitID(iEMode, inWS);
  // initialize units conversion
  unitsConverter.initialize(WSDescription, convUnitsID);
  // initialize MD transformation
  pQtransf->initialize(WSDescription);

  //
  long nHist = static_cast<long>(inWS->getNumberHistograms());
  auto detIDMap =
      WSDescription.m_PreprDetTable->getColVector<size_t>("detIDMap");

  // vector to place transformed coordinates;
  std::vector<coord_t> locCoord(nDims);

  pQtransf->calcGenericVariables(locCoord, nDims);
  // PRAGMA_OMP(parallel for reduction(||:rangeChanged))
  for (long i = 0; i < nHist; i++) {
    // get valid spectrum number
    size_t iSpctr = detIDMap[i];

    // update unit conversion according to current spectra
    unitsConverter.updateConversion(iSpctr);
    // update coordinate transformation according to the spectra
    pQtransf->calcYDepCoordinates(locCoord, iSpctr);

    // get the range of the input data in the spectra
    auto source_range = inWS->getSpectrum(iSpctr)->getXDataRange();

    // extract part of this range which has well defined unit conversion
    source_range = unitsConverter.getConversionRange(source_range.first,
                                                     source_range.second);

    double x1 = unitsConverter.convertUnits(source_range.first);
    double x2 = unitsConverter.convertUnits(source_range.second);

    std::vector<double> range = pQtransf->getExtremumPoints(x1, x2, iSpctr);
    // transform coordinates
    for (size_t k = 0; k < range.size(); k++) {

      pQtransf->calcMatrixCoord(range[k], locCoord, signal, errorSq);
      // identify min-max ranges for current spectrum
      for (size_t j = 0; j < nDims; j++) {
        if (locCoord[j] < MinValues[j])
          MinValues[j] = locCoord[j];
        if (locCoord[j] > MaxValues[j])
          MaxValues[j] = locCoord[j];
      }
    }
  }
}
} // namespace MDAlgorithms
} // namespace Mantid