/**
 * Disable points in the workpsace in the way that points which are not included
 * in any of specified
 * sections are not used when fitting given workspace
 * @param ws :: Workspace to disable points in
 * @param sections :: Section we want to use for fitting
 */
void ALCBaselineModellingModel::disableUnwantedPoints(
    MatrixWorkspace_sptr ws,
    const std::vector<IALCBaselineModellingModel::Section> &sections) {
  // Whether point with particular index should be disabled
  const size_t numBins = ws->blocksize();
  std::vector<bool> toDisable(numBins, true);

  // Find points which are in at least one section, and exclude them from
  // disable list
  for (size_t i = 0; i < numBins; ++i) {
    for (auto it = sections.begin(); it != sections.end(); ++it) {
      if (ws->x(0)[i] >= it->first && ws->x(0)[i] <= it->second) {
        toDisable[i] = false;
        break; // No need to check other sections
      }
    }
  }

  // XXX: Points are disabled by settings their errors to very high value. This
  // makes those
  //      points to have very low weights during the fitting, effectively
  //      disabling them.

  const double DISABLED_ERR = std::numeric_limits<double>::max();

  // Disable chosen points
  for (size_t i = 0; i < numBins; ++i) {
    if (toDisable[i]) {
      ws->mutableE(0)[i] = DISABLED_ERR;
    }
  }
}
/** Rebins the distributions and sets error values.
 */
MatrixWorkspace_sptr
VesuvioL1ThetaResolution::processDistribution(MatrixWorkspace_sptr ws,
                                              const double binWidth) {
  const size_t numHist = ws->getNumberHistograms();

  double xMin(DBL_MAX);
  double xMax(DBL_MIN);
  for (size_t i = 0; i < numHist; i++) {
    auto &x = ws->x(i);
    xMin = std::min(xMin, x.front());
    xMax = std::max(xMax, x.back());
  }

  std::stringstream binParams;
  binParams << xMin << "," << binWidth << "," << xMax;

  IAlgorithm_sptr rebin = AlgorithmManager::Instance().create("Rebin");
  rebin->initialize();
  rebin->setChild(true);
  rebin->setLogging(false);
  rebin->setProperty("InputWorkspace", ws);
  rebin->setProperty("OutputWorkspace", "__rebin");
  rebin->setProperty("Params", binParams.str());
  rebin->execute();
  ws = rebin->getProperty("OutputWorkspace");

  for (size_t i = 0; i < numHist; i++) {
    auto &y = ws->y(i);
    auto &e = ws->mutableE(i);
    std::transform(y.begin(), y.end(), e.begin(),
                   [](double x) { return sqrt(x); });
  }

  return ws;
}
Beispiel #3
0
/** Create an output workspace of the appropriate (histogram or event) type
* and
* copy over the data
*  @param inputWS The input workspace
*/
API::MatrixWorkspace_sptr ConvertUnits::setupOutputWorkspace(
    const API::MatrixWorkspace_const_sptr inputWS) {
  MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");

  // If input and output workspaces are NOT the same, create a new workspace
  // for
  // the output
  if (outputWS != inputWS) {
    outputWS = inputWS->clone();
  }

  if (!m_inputEvents && m_distribution) {
    // Loop over the histograms (detector spectra)
    Progress prog(this, 0.0, 0.2, m_numberOfSpectra);
    PARALLEL_FOR_IF(Kernel::threadSafe(*outputWS))
    for (int64_t i = 0; i < static_cast<int64_t>(m_numberOfSpectra); ++i) {
      PARALLEL_START_INTERUPT_REGION
      // Take the bin width dependency out of the Y & E data
      auto &X = outputWS->x(i);
      auto &Y = outputWS->mutableY(i);
      auto &E = outputWS->mutableE(i);
      for (size_t j = 0; j < outputWS->blocksize(); ++j) {
        const double width = std::abs(X[j + 1] - X[j]);
        Y[j] *= width;
        E[j] *= width;
      }

      prog.report("Convert to " + m_outputUnit->unitID());
      PARALLEL_END_INTERUPT_REGION
    }
    PARALLEL_CHECK_INTERUPT_REGION
  }
/**Convert a binned workspace to point data
 *
 * @param workspace :: The input workspace
 * @return the converted workspace containing point data
 */
MatrixWorkspace_sptr
SplineInterpolation::convertBinnedData(MatrixWorkspace_sptr workspace) const {
  if (workspace->isHistogramData()) {
    const size_t histNo = workspace->getNumberHistograms();
    const size_t size = workspace->y(0).size();

    // make a new workspace for the point data
    MatrixWorkspace_sptr pointWorkspace =
        WorkspaceFactory::Instance().create(workspace, histNo, size, size);

    // loop over each histogram
    for (size_t i = 0; i < histNo; ++i) {
      const auto &xValues = workspace->x(i);
      pointWorkspace->setSharedY(i, workspace->sharedY(i));

      auto &newXValues = pointWorkspace->mutableX(i);

      // set x values to be average of bin bounds
      for (size_t j = 0; j < size; ++j) {
        newXValues[j] = (xValues[j] + xValues[j + 1]) / 2;
      }
    }

    return pointWorkspace;
  }

  return workspace;
}
Beispiel #5
0
MatrixWorkspace_sptr
CalculateIqt::removeInvalidData(MatrixWorkspace_sptr workspace) {
  auto binning = (workspace->blocksize() + 1) / 2;
  auto binV = workspace->x(0)[binning];
  workspace = cropWorkspace(workspace, binV);
  return replaceSpecialValues(workspace);
}
/**
 * Sum counts from the input workspace in lambda along lines of constant Q by
 * projecting to "virtual lambda" at a reference angle twoThetaR.
 *
 * @param detectorWS [in] :: the input workspace in wavelength
 * @return :: the output workspace in wavelength
 */
MatrixWorkspace_sptr
ReflectometryReductionOne2::sumInQ(MatrixWorkspace_sptr detectorWS) {

  // Construct the output array in virtual lambda
  MatrixWorkspace_sptr IvsLam = constructIvsLamWS(detectorWS);

  // Loop through each input group (and corresponding output spectrum)
  const size_t numGroups = detectorGroups().size();
  for (size_t groupIdx = 0; groupIdx < numGroups; ++groupIdx) {
    auto &detectors = detectorGroups()[groupIdx];
    auto &outputE = IvsLam->dataE(groupIdx);

    // Loop through each spectrum in the detector group
    for (auto spIdx : detectors) {
      // Get the angle of this detector and its size in twoTheta
      const double twoTheta = getDetectorTwoTheta(m_spectrumInfo, spIdx);
      const double bTwoTheta = getDetectorTwoThetaRange(spIdx);

      // Check X length is Y length + 1
      const auto &inputX = detectorWS->x(spIdx);
      const auto &inputY = detectorWS->y(spIdx);
      const auto &inputE = detectorWS->e(spIdx);
      if (inputX.size() != inputY.size() + 1) {
        throw std::runtime_error(
            "Expected input workspace to be histogram data (got X len=" +
            std::to_string(inputX.size()) +
            ", Y len=" + std::to_string(inputY.size()) + ")");
      }

      // Create a vector for the projected errors for this spectrum.
      // (Output Y values can simply be accumulated directly into the output
      // workspace, but for error values we need to create a separate error
      // vector for the projected errors from each input spectrum and then
      // do an overall sum in quadrature.)
      std::vector<double> projectedE(outputE.size(), 0.0);

      // Process each value in the spectrum
      const int ySize = static_cast<int>(inputY.size());
      for (int inputIdx = 0; inputIdx < ySize; ++inputIdx) {
        // Do the summation in Q
        sumInQProcessValue(inputIdx, twoTheta, bTwoTheta, inputX, inputY,
                           inputE, detectors, groupIdx, IvsLam, projectedE);
      }

      // Sum errors in quadrature
      const int eSize = static_cast<int>(outputE.size());
      for (int outIdx = 0; outIdx < eSize; ++outIdx) {
        outputE[outIdx] += projectedE[outIdx] * projectedE[outIdx];
      }
    }

    // Take the square root of all the accumulated squared errors for this
    // detector group. Assumes Gaussian errors
    double (*rs)(double) = std::sqrt;
    std::transform(outputE.begin(), outputE.end(), outputE.begin(), rs);
  }

  return IvsLam;
}
Beispiel #7
0
/**
 * Extracts time bin width from workspace parameter
 *
 * The method uses the difference between first and second x-value of the
 * first spectrum as time bin width. If the workspace does not contain proper
 * data (0 spectra or less than 2 x-values), the method throws an
 * std::invalid_argument-exception Otherwise it calls setDeltaT.
 *
 * @param matrixWorkspace :: MatrixWorkspace with at least one spectrum with
 *at
 *                           least two x-values.
 */
void PoldiFitPeaks2D::setDeltaTFromWorkspace(
    const MatrixWorkspace_sptr &matrixWorkspace) {
  if (matrixWorkspace->getNumberHistograms() < 1) {
    throw std::invalid_argument("MatrixWorkspace does not contain any data.");
  }

  auto &xData = matrixWorkspace->x(0);

  if (xData.size() < 2) {
    throw std::invalid_argument(
        "Cannot process MatrixWorkspace with less than 2 x-values.");
  }

  // difference between first and second x-value is assumed to be the bin
  // width.
  setDeltaT(matrixWorkspace->x(0)[1] - matrixWorkspace->x(0)[0]);
}
/**
 * Construct an "empty" output workspace in virtual-lambda for summation in Q.
 * The workspace will have the same x values as the input workspace but the y
 * values will all be zero.
 *
 * @return : a 1D workspace where y values are all zero
 */
MatrixWorkspace_sptr
ReflectometryReductionOne2::constructIvsLamWS(MatrixWorkspace_sptr detectorWS) {

  // There is one output spectrum for each detector group
  const size_t numGroups = detectorGroups().size();
  // Calculate the number of bins based on the min/max wavelength, using
  // the same bin width as the input workspace
  const double binWidth = (detectorWS->x(0).back() - detectorWS->x(0).front()) /
                          static_cast<double>(detectorWS->blocksize());
  const int numBins = static_cast<int>(
      std::ceil((wavelengthMax() - wavelengthMin()) / binWidth));
  // Construct the histogram with these X values. Y and E values are zero.
  const BinEdges xValues(numBins, LinearGenerator(wavelengthMin(), binWidth));
  // Create the output workspace
  MatrixWorkspace_sptr outputWS = WorkspaceFactory::Instance().create(
      detectorWS, numGroups, numBins, numBins - 1);

  // Loop through each detector group in the input
  for (size_t groupIdx = 0; groupIdx < numGroups; ++groupIdx) {
    // Get the detectors in this group
    auto &detectors = detectorGroups()[groupIdx];
    // Set the x values for this spectrum
    outputWS->setBinEdges(groupIdx, xValues);
    // Set the detector ID from the twoThetaR detector.
    const size_t twoThetaRIdx = twoThetaRDetectorIdx(detectors);
    auto &outSpec = outputWS->getSpectrum(groupIdx);
    const detid_t twoThetaRDetID =
        m_spectrumInfo->detector(twoThetaRIdx).getID();
    outSpec.clearDetectorIDs();
    outSpec.addDetectorID(twoThetaRDetID);
    // Set the spectrum number from the twoThetaR detector
    SpectrumNumber specNum =
        detectorWS->indexInfo().spectrumNumber(twoThetaRIdx);
    auto indexInf = outputWS->indexInfo();
    indexInf.setSpectrumNumbers(specNum, specNum);
    outputWS->setIndexInfo(indexInf);
  }

  return outputWS;
}
MatrixWorkspace_sptr
JoinISISPolarizationEfficiencies::interpolateHistogramWorkspace(
    MatrixWorkspace_sptr ws, size_t const maxSize) {
  ws->setDistribution(true);
  auto const &x = ws->x(0);
  auto const dX = (x.back() - x.front()) / double(maxSize);
  std::vector<double> params(2 * maxSize + 1);
  for (size_t i = 0; i < maxSize; ++i) {
    params[2 * i] = x.front() + dX * double(i);
    params[2 * i + 1] = dX;
  }
  params.back() = x.back();
  auto alg = createChildAlgorithm("InterpolatingRebin");
  alg->setProperty("InputWorkspace", ws);
  alg->setProperty("Params", params);
  alg->setProperty("OutputWorkspace", "dummy");
  alg->execute();
  MatrixWorkspace_sptr interpolatedWS = alg->getProperty("OutputWorkspace");
  assert(interpolatedWS->y(0).size() == maxSize);
  assert(interpolatedWS->x(0).size() == maxSize + 1);
  return interpolatedWS;
}
MatrixWorkspace_sptr
JoinISISPolarizationEfficiencies::interpolatePointDataWorkspace(
    MatrixWorkspace_sptr ws, size_t const maxSize) {
  auto const &x = ws->x(0);
  auto const startX = x.front();
  auto const endX = x.back();
  Counts yVals(maxSize, 0.0);
  auto const dX = (endX - startX) / double(maxSize - 1);
  Points xVals(maxSize, LinearGenerator(startX, dX));
  auto newHisto = Histogram(xVals, yVals);
  interpolateLinearInplace(ws->histogram(0), newHisto);
  auto interpolatedWS = boost::make_shared<Workspace2D>();
  interpolatedWS->initialize(1, newHisto);
  assert(interpolatedWS->y(0).size() == maxSize);
  return interpolatedWS;
}
double ReflectometryReductionOne2::findIvsLamRangeMin(
    MatrixWorkspace_sptr detectorWS, const std::vector<size_t> &detectors,
    const double lambda) {
  double projectedMin = 0.0;

  const size_t spIdx = findIvsLamRangeMinDetector(detectors);
  const double twoTheta = getDetectorTwoTheta(m_spectrumInfo, spIdx);
  const double bTwoTheta = getDetectorTwoThetaRange(spIdx);

  // For bLambda, use the average bin size for this spectrum
  const auto &xValues = detectorWS->x(spIdx);
  double bLambda = (xValues[xValues.size() - 1] - xValues[0]) /
                   static_cast<int>(xValues.size());
  double dummy = 0.0;
  getProjectedLambdaRange(lambda, twoTheta, bLambda, bTwoTheta, detectors,
                          projectedMin, dummy, m_partialBins);
  return projectedMin;
}
/** Convert a workspace to Q
 *
 * @param inputWS : The input workspace (in wavelength) to convert to Q
 * @return : output workspace in Q
 */
MatrixWorkspace_sptr
ReflectometryReductionOne2::convertToQ(MatrixWorkspace_sptr inputWS) {
  bool const moreThanOneDetector = inputWS->getDetector(0)->nDets() > 1;
  bool const shouldCorrectAngle =
      !(*getProperty("ThetaIn")).isDefault() && !summingInQ();
  if (shouldCorrectAngle && moreThanOneDetector) {
    if (inputWS->getNumberHistograms() > 1) {
      throw std::invalid_argument(
          "Expected a single group in "
          "ProcessingInstructions to be able to "
          "perform angle correction, found " +
          std::to_string(inputWS->getNumberHistograms()));
    }
    MatrixWorkspace_sptr IvsQ = inputWS->clone();
    auto &XOut0 = IvsQ->mutableX(0);
    const auto &XIn0 = inputWS->x(0);
    double const theta = getProperty("ThetaIn");
    double const factor = 4.0 * M_PI * sin(theta * M_PI / 180.0);
    std::transform(XIn0.rbegin(), XIn0.rend(), XOut0.begin(),
                   [factor](double x) { return factor / x; });
    auto &Y0 = IvsQ->mutableY(0);
    auto &E0 = IvsQ->mutableE(0);
    std::reverse(Y0.begin(), Y0.end());
    std::reverse(E0.begin(), E0.end());
    IvsQ->getAxis(0)->unit() =
        UnitFactory::Instance().create("MomentumTransfer");
    return IvsQ;
  } else {
    auto convertUnits = this->createChildAlgorithm("ConvertUnits");
    convertUnits->initialize();
    convertUnits->setProperty("InputWorkspace", inputWS);
    convertUnits->setProperty("Target", "MomentumTransfer");
    convertUnits->setProperty("AlignBins", false);
    convertUnits->execute();
    MatrixWorkspace_sptr IvsQ = convertUnits->getProperty("OutputWorkspace");
    return IvsQ;
  }
}
void TOFSANSResolutionByPixel::exec() {
  MatrixWorkspace_sptr inWS = getProperty("InputWorkspace");
  double deltaR = getProperty("DeltaR");
  double R1 = getProperty("SourceApertureRadius");
  double R2 = getProperty("SampleApertureRadius");
  const bool doGravity = getProperty("AccountForGravity");

  // Check the input
  checkInput(inWS);

  // Setup outputworkspace
  auto outWS = setupOutputWorkspace(inWS);

  // Convert to meters
  deltaR /= 1000.0;
  R1 /= 1000.0;
  R2 /= 1000.0;

  // The moderator workspace needs to match the data workspace
  // in terms of wavelength binning
  const MatrixWorkspace_sptr sigmaModeratorVSwavelength =
      getModeratorWorkspace(inWS);

  // create interpolation table from sigmaModeratorVSwavelength
  Kernel::Interpolation lookUpTable;

  const auto &xInterpolate = sigmaModeratorVSwavelength->points(0);
  const auto &yInterpolate = sigmaModeratorVSwavelength->y(0);

  // prefer the input to be a pointworkspace and create interpolation function
  if (sigmaModeratorVSwavelength->isHistogramData()) {
    g_log.notice() << "mid-points of SigmaModerator histogram bins will be "
                      "used for interpolation.";
  }

  for (size_t i = 0; i < xInterpolate.size(); ++i) {
    lookUpTable.addPoint(xInterpolate[i], yInterpolate[i]);
  }

  // Calculate the L1 distance
  const V3D samplePos = inWS->getInstrument()->getSample()->getPos();
  const V3D sourcePos = inWS->getInstrument()->getSource()->getPos();
  const V3D SSD = samplePos - sourcePos;
  const double L1 = SSD.norm();

  // Get the collimation length
  double LCollim = getProperty("CollimationLength");

  if (LCollim == 0.0) {
    auto collimationLengthEstimator = SANSCollimationLengthEstimator();
    LCollim = collimationLengthEstimator.provideCollimationLength(inWS);
    g_log.information() << "No collimation length was specified. A default "
                           "collimation length was estimated to be " << LCollim
                        << '\n';
  } else {
    g_log.information() << "The collimation length is  " << LCollim << '\n';
  }

  const int numberOfSpectra = static_cast<int>(inWS->getNumberHistograms());
  Progress progress(this, 0.0, 1.0, numberOfSpectra);

  const auto &spectrumInfo = inWS->spectrumInfo();
  for (int i = 0; i < numberOfSpectra; i++) {
    IDetector_const_sptr det;
    if (!spectrumInfo.hasDetectors(i)) {
      g_log.information() << "Workspace index " << i
                          << " has no detector assigned to it - discarding\n";
      continue;
    }
    // If no detector found or if it's masked or a monitor, skip onto the next
    // spectrum
    if (spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i))
      continue;

    const double L2 = spectrumInfo.l2(i);
    TOFSANSResolutionByPixelCalculator calculator;
    const double waveLengthIndependentFactor =
        calculator.getWavelengthIndependentFactor(R1, R2, deltaR, LCollim, L2);

    // Multiplicative factor to go from lambda to Q
    // Don't get fooled by the function name...
    const double theta = spectrumInfo.twoTheta(i);
    double sinTheta = sin(0.5 * theta);
    double factor = 4.0 * M_PI * sinTheta;

    const auto &xIn = inWS->x(i);
    const size_t xLength = xIn.size();

    // Gravity correction
    std::unique_ptr<GravitySANSHelper> grav;
    if (doGravity) {
      grav = Kernel::make_unique<GravitySANSHelper>(spectrumInfo, i,
                                                    getProperty("ExtraLength"));
    }

    // Get handles on the outputWorkspace
    auto &yOut = outWS->mutableY(i);
    // for each wavelenght bin of each pixel calculate a q-resolution
    for (size_t j = 0; j < xLength - 1; j++) {
      // use the midpoint of each bin
      const double wl = (xIn[j + 1] + xIn[j]) / 2.0;
      // Calculate q. Alternatively q could be calculated using ConvertUnit
      // If we include a gravity correction we need to adjust sinTheta
      // for each wavelength (in Angstrom)
      if (doGravity) {
        double sinThetaGrav = grav->calcSinTheta(wl);
        factor = 4.0 * M_PI * sinThetaGrav;
      }
      const double q = factor / wl;

      // wavelenght spread from bin assumed to be
      const double sigmaSpreadFromBin = xIn[j + 1] - xIn[j];

      // Get the uncertainty in Q
      auto sigmaQ = calculator.getSigmaQValue(lookUpTable.value(wl),
                                              waveLengthIndependentFactor, q,
                                              wl, sigmaSpreadFromBin, L1, L2);

      // Insert the Q value and the Q resolution into the outputworkspace
      yOut[j] = sigmaQ;
    }
    progress.report("Computing Q resolution");
  }

  // Set the y axis label
  outWS->setYUnitLabel("QResolution");

  setProperty("OutputWorkspace", outWS);
}
Beispiel #14
0
/**Executes the main part of the algorithm that handles the conversion of the
* units
* @param inputWS :: the input workspace that will be converted
* @throw std::runtime_error :: If the workspace has invalid X axis binning
* @return A pointer to a MatrixWorkspace_sptr that contains the converted units
*/
MatrixWorkspace_sptr
ConvertUnits::executeUnitConversion(const API::MatrixWorkspace_sptr inputWS) {

  // A WS holding BinEdges cannot have less than 2 values, as a bin has
  // 2 edges, having less than 2 values would mean that the WS contains Points
  if (inputWS->x(0).size() < 2) {
    std::stringstream msg;
    msg << "Input workspace has invalid X axis binning parameters. Should "
           "have "
           "at least 2 values. Found " << inputWS->x(0).size() << ".";
    throw std::runtime_error(msg.str());
  }
  if (inputWS->x(0).front() > inputWS->x(0).back() ||
      inputWS->x(m_numberOfSpectra / 2).front() >
          inputWS->x(m_numberOfSpectra / 2).back())
    throw std::runtime_error("Input workspace has invalid X axis binning "
                             "parameters. X values should be increasing.");

  MatrixWorkspace_sptr outputWS;
  // Check whether there is a quick conversion available
  double factor, power;
  if (m_inputUnit->quickConversion(*m_outputUnit, factor, power))
  // If test fails, could also check whether a quick conversion in the
  // opposite
  // direction has been entered
  {
    outputWS = this->convertQuickly(inputWS, factor, power);
  } else {
    outputWS = this->convertViaTOF(m_inputUnit, inputWS);
  }

  // If the units conversion has flipped the ascending direction of X, reverse
  // all the vectors
  if (!outputWS->x(0).empty() &&
      (outputWS->x(0).front() > outputWS->x(0).back() ||
       outputWS->x(m_numberOfSpectra / 2).front() >
           outputWS->x(m_numberOfSpectra / 2).back())) {
    this->reverse(outputWS);
  }

  // Need to lop bins off if converting to energy transfer.
  // Don't do for EventWorkspaces, where you can easily rebin to recover the
  // situation without losing information
  /* This is an ugly test - could be made more general by testing for DBL_MAX
  values at the ends of all spectra, but that would be less efficient */
  if (m_outputUnit->unitID().find("Delta") == 0 && !m_inputEvents)
    outputWS = this->removeUnphysicalBins(outputWS);

  // Rebin the data to common bins if requested, and if necessary
  bool alignBins = getProperty("AlignBins");
  if (alignBins && !WorkspaceHelpers::commonBoundaries(outputWS))
    outputWS = this->alignBins(outputWS);

  // If appropriate, put back the bin width division into Y/E.
  if (m_distribution && !m_inputEvents) // Never do this for event workspaces
  {
    this->putBackBinWidth(outputWS);
  }

  return outputWS;
}
Beispiel #15
0
//------------------------------------------------------------------------------------------------
/// @cond
// Local function used within validateInputs() below in a call to
// std::list::sort(compare)
// to order the input workspaces by the start of their frame (i.e. the first X
// value).
static bool compare(MatrixWorkspace_sptr first, MatrixWorkspace_sptr second) {
  return (first->x(0).front() < second->x(0).front());
}
/** Convert the workspace units using TOF as an intermediate step in the
* conversion
* @param fromUnit :: The unit of the input workspace
* @param inputWS :: The input workspace
* @returns A shared pointer to the output workspace
*/
MatrixWorkspace_sptr ConvertUnitsUsingDetectorTable::convertViaTOF(
    Kernel::Unit_const_sptr fromUnit, API::MatrixWorkspace_const_sptr inputWS) {
  using namespace Geometry;

  // Let's see if we are using a TableWorkspace to override parameters
  TableWorkspace_sptr paramWS = getProperty("DetectorParameters");

  // See if we have supplied a DetectorParameters Workspace
  // TODO: Check if paramWS is NULL and if so throw an exception

  //      const std::string l1ColumnLabel("l1");

  // Let's check all the columns exist and are readable
  try {
    auto spectraColumnTmp = paramWS->getColumn("spectra");
    auto l1ColumnTmp = paramWS->getColumn("l1");
    auto l2ColumnTmp = paramWS->getColumn("l2");
    auto twoThetaColumnTmp = paramWS->getColumn("twotheta");
    auto efixedColumnTmp = paramWS->getColumn("efixed");
    auto emodeColumnTmp = paramWS->getColumn("emode");
  } catch (...) {
    throw Exception::InstrumentDefinitionError(
        "DetectorParameter TableWorkspace is not defined correctly.");
  }

  // Now let's take a reference to the vectors.
  const auto &l1Column = paramWS->getColVector<double>("l1");
  const auto &l2Column = paramWS->getColVector<double>("l2");
  const auto &twoThetaColumn = paramWS->getColVector<double>("twotheta");
  const auto &efixedColumn = paramWS->getColVector<double>("efixed");
  const auto &emodeColumn = paramWS->getColVector<int>("emode");
  const auto &spectraColumn = paramWS->getColVector<int>("spectra");

  Progress prog(this, 0.2, 1.0, m_numberOfSpectra);
  int64_t numberOfSpectra_i =
      static_cast<int64_t>(m_numberOfSpectra); // cast to make openmp happy

  // Get the unit object for each workspace
  Kernel::Unit_const_sptr outputUnit = m_outputUnit;
  std::vector<double> emptyVec;
  int failedDetectorCount = 0;

  // Perform Sanity Validation before creating workspace
  size_t checkIndex = 0;
  int checkSpecNo = inputWS->getDetector(checkIndex)->getID();
  auto checkSpecIter =
      std::find(spectraColumn.begin(), spectraColumn.end(), checkSpecNo);
  if (checkSpecIter != spectraColumn.end()) {
    size_t detectorRow = std::distance(spectraColumn.begin(), checkSpecIter);
    // copy the X values for the check
    auto checkXValues = inputWS->readX(checkIndex);
    // Convert the input unit to time-of-flight
    auto checkFromUnit = std::unique_ptr<Unit>(fromUnit->clone());
    auto checkOutputUnit = std::unique_ptr<Unit>(outputUnit->clone());
    double checkdelta = 0;
    checkFromUnit->toTOF(checkXValues, emptyVec, l1Column[detectorRow],
                         l2Column[detectorRow], twoThetaColumn[detectorRow],
                         emodeColumn[detectorRow], efixedColumn[detectorRow],
                         checkdelta);
    // Convert from time-of-flight to the desired unit
    checkOutputUnit->fromTOF(checkXValues, emptyVec, l1Column[detectorRow],
                             l2Column[detectorRow], twoThetaColumn[detectorRow],
                             emodeColumn[detectorRow],
                             efixedColumn[detectorRow], checkdelta);
  }

  // create the output workspace
  MatrixWorkspace_sptr outputWS = this->setupOutputWorkspace(inputWS);
  EventWorkspace_sptr eventWS =
      boost::dynamic_pointer_cast<EventWorkspace>(outputWS);
  assert(static_cast<bool>(eventWS) == m_inputEvents); // Sanity check

  // TODO: Check why this parallel stuff breaks
  // Loop over the histograms (detector spectra)
  // PARALLEL_FOR_IF(Kernel::threadSafe(*outputWS))
  for (int64_t i = 0; i < numberOfSpectra_i; ++i) {

    // Lets find what row this spectrum Number appears in our detector table.

    // PARALLEL_START_INTERUPT_REGION

    std::size_t wsid = i;

    try {

      double deg2rad = M_PI / 180.;

      auto det = outputWS->getDetector(i);
      int specNo = det->getID();

      // int spectraNumber = static_cast<int>(spectraColumn->toDouble(i));
      // wsid = outputWS->getIndexFromSpectrumNumber(spectraNumber);
      g_log.debug() << "###### Spectra #" << specNo
                    << " ==> Workspace ID:" << wsid << '\n';

      // Now we need to find the row that contains this spectrum
      std::vector<int>::const_iterator specIter;

      specIter = std::find(spectraColumn.begin(), spectraColumn.end(), specNo);
      if (specIter != spectraColumn.end()) {
        const size_t detectorRow =
            std::distance(spectraColumn.begin(), specIter);
        const double l1 = l1Column[detectorRow];
        const double l2 = l2Column[detectorRow];
        const double twoTheta = twoThetaColumn[detectorRow] * deg2rad;
        const double efixed = efixedColumn[detectorRow];
        const int emode = emodeColumn[detectorRow];

        if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
          g_log.debug() << "specNo from detector table = "
                        << spectraColumn[detectorRow] << '\n';

          g_log.debug() << "###### Spectra #" << specNo
                        << " ==> Det Table Row:" << detectorRow << '\n';

          g_log.debug() << "\tL1=" << l1 << ",L2=" << l2 << ",TT=" << twoTheta
                        << ",EF=" << efixed << ",EM=" << emode << '\n';
        }

        // Make local copies of the units. This allows running the loop in
        // parallel
        auto localFromUnit = std::unique_ptr<Unit>(fromUnit->clone());
        auto localOutputUnit = std::unique_ptr<Unit>(outputUnit->clone());
        /// @todo Don't yet consider hold-off (delta)
        const double delta = 0.0;
        std::vector<double> values(outputWS->x(wsid).begin(),
                                   outputWS->x(wsid).end());

        // Convert the input unit to time-of-flight
        localFromUnit->toTOF(values, emptyVec, l1, l2, twoTheta, emode, efixed,
                             delta);
        // Convert from time-of-flight to the desired unit
        localOutputUnit->fromTOF(values, emptyVec, l1, l2, twoTheta, emode,
                                 efixed, delta);

        outputWS->mutableX(wsid) = std::move(values);

        // EventWorkspace part, modifying the EventLists.
        if (m_inputEvents) {
          eventWS->getSpectrum(wsid)
              .convertUnitsViaTof(localFromUnit.get(), localOutputUnit.get());
        }

      } else {
        // Not found
        failedDetectorCount++;
        outputWS->maskWorkspaceIndex(wsid);
      }

    } catch (Exception::NotFoundError &) {
      // Get to here if exception thrown when calculating distance to detector
      failedDetectorCount++;
      // Since you usually (always?) get to here when there's no attached
      // detectors, this call is
      // the same as just zeroing out the data (calling clearData on the
      // spectrum)
      outputWS->maskWorkspaceIndex(i);
    }

    prog.report("Convert to " + m_outputUnit->unitID());
    // PARALLEL_END_INTERUPT_REGION
  } // loop over spectra
  // PARALLEL_CHECK_INTERUPT_REGION

  if (failedDetectorCount != 0) {
    g_log.information() << "Something went wrong for " << failedDetectorCount
                        << " spectra. Masking spectrum.\n";
  }
  if (m_inputEvents)
    eventWS->clearMRU();

  return outputWS;
}