Beispiel #1
0
/**
 * Sum counts from the input workspace in lambda along lines of constant Q by
 * projecting to "virtual lambda" at a reference angle.
 *
 * @param detectorWS [in] :: the input workspace in wavelength
 * @param indices [in] :: an index set defining the foreground histograms
 * @return :: the single histogram output workspace in wavelength
 */
API::MatrixWorkspace_sptr
ReflectometrySumInQ::sumInQ(const API::MatrixWorkspace &detectorWS,
                            const Indexing::SpectrumIndexSet &indices) {

  const auto spectrumInfo = detectorWS.spectrumInfo();
  const auto refAngles = referenceAngles(spectrumInfo);
  // Construct the output workspace in virtual lambda
  API::MatrixWorkspace_sptr IvsLam =
      constructIvsLamWS(detectorWS, indices, refAngles);
  auto &outputE = IvsLam->dataE(0);
  // Loop through each spectrum in the detector group
  for (auto spIdx : indices) {
    if (spectrumInfo.isMasked(spIdx) || spectrumInfo.isMonitor(spIdx)) {
      continue;
    }
    // Get the size of this detector in twoTheta
    const auto twoThetaRange = twoThetaWidth(spIdx, spectrumInfo);
    // Check X length is Y length + 1
    const auto inputBinEdges = detectorWS.binEdges(spIdx);
    const auto inputCounts = detectorWS.counts(spIdx);
    const auto inputStdDevs = detectorWS.countStandardDeviations(spIdx);
    // 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>(inputCounts.size());
    for (int inputIdx = 0; inputIdx < ySize; ++inputIdx) {
      // Do the summation in Q
      processValue(inputIdx, twoThetaRange, refAngles, inputBinEdges,
                   inputCounts, inputStdDevs, *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 #2
0
/**
 * Return a pair of (minimum Q, maximum Q) for given
 * indirect geometry workspace. Estimates the Q range from all detectors.
 * If workspace contains grouped detectors/not all detectors are linked
 * to a spectrum, the returned interval may be larger than actually needed.
 * @param ws a workspace
 * @param minE minimum energy transfer in ws
 * @param maxE maximum energy transfer in ws
 * @return a pair containing global minimun and maximum Q
 */
std::pair<double, double>
SofQCommon::qBinHintsIndirect(const API::MatrixWorkspace &ws, const double minE,
                              const double maxE) const {
  using namespace Mantid::PhysicalConstants;
  auto minQ = std::numeric_limits<double>::max();
  auto maxQ = std::numeric_limits<double>::lowest();
  const auto &detectorInfo = ws.detectorInfo();
  for (size_t i = 0; i < detectorInfo.size(); ++i) {
    if (detectorInfo.isMasked(i) || detectorInfo.isMonitor(i)) {
      continue;
    }
    const auto twoTheta = detectorInfo.twoTheta(i);
    const auto &det = detectorInfo.detector(i);
    const auto Q1 = indirectQ(minE, twoTheta, &det);
    const auto Q2 = indirectQ(maxE, twoTheta, &det);
    const auto minmaxQ = std::minmax(Q1, Q2);
    if (minmaxQ.first < minQ) {
      minQ = minmaxQ.first;
    }
    if (minmaxQ.second > maxQ) {
      maxQ = minmaxQ.second;
    }
  }
  if (minQ == std::numeric_limits<double>::max()) {
    throw std::runtime_error("Could not determine Q binning: workspace does "
                             "not contain usable spectra.");
  }
  return std::make_pair(minQ, maxQ);
}
Beispiel #3
0
/**
 * Return a pair of (minimum Q, maximum Q) for given
 * direct geometry workspace.
 * @param ws a workspace
 * @param minE minimum energy transfer in ws
 * @param maxE maximum energy transfer in ws
 * @return a pair containing global minimun and maximum Q
 */
std::pair<double, double>
SofQCommon::qBinHintsDirect(const API::MatrixWorkspace &ws, const double minE,
                            const double maxE) const {
  using namespace Mantid::PhysicalConstants;
  auto minTwoTheta = std::numeric_limits<double>::max();
  auto maxTwoTheta = std::numeric_limits<double>::lowest();
  const auto &spectrumInfo = ws.spectrumInfo();
  for (size_t i = 0; i < spectrumInfo.size(); ++i) {
    if (spectrumInfo.isMasked(i) || spectrumInfo.isMonitor(i)) {
      continue;
    }
    const auto twoTheta = spectrumInfo.twoTheta(i);
    if (twoTheta < minTwoTheta) {
      minTwoTheta = twoTheta;
    }
    if (twoTheta > maxTwoTheta) {
      maxTwoTheta = twoTheta;
    }
  }
  if (minTwoTheta == std::numeric_limits<double>::max()) {
    throw std::runtime_error("Could not determine Q binning: workspace does "
                             "not contain usable spectra.");
  }
  std::array<double, 4> q;
  q[0] = directQ(minE, minTwoTheta);
  q[1] = directQ(minE, maxTwoTheta);
  q[2] = directQ(maxE, minTwoTheta);
  q[3] = directQ(maxE, maxTwoTheta);
  const auto minmaxQ = std::minmax_element(q.cbegin(), q.cend());
  return std::make_pair(*minmaxQ.first, *minmaxQ.second);
}
Beispiel #4
0
/**
 * Return the wavelength range of the output histogram.
 * @param detectorWS [in] :: the input workspace
 * @param indices [in] :: the workspace indices of foreground histograms
 * @param refAngles [in] :: the reference angles
 * @return :: the minimum and maximum virtual wavelengths
 */
ReflectometrySumInQ::MinMax ReflectometrySumInQ::findWavelengthMinMax(
    const API::MatrixWorkspace &detectorWS,
    const Indexing::SpectrumIndexSet &indices, const Angles &refAngles) {
  const API::SpectrumInfo &spectrumInfo = detectorWS.spectrumInfo();
  // Get the new max and min X values of the projected (virtual) lambda range
  const bool includePartialBins = getProperty(Prop::PARTIAL_BINS);
  // Find minimum and maximum 2thetas and the corresponding indices.
  // It cannot be assumed that 2theta increases with indices, check for example
  // D17 at ILL
  MinMax inputLambdaRange;
  MinMax inputTwoThetaRange;
  for (const auto i : indices) {
    const auto twoThetas = twoThetaWidth(i, spectrumInfo);
    inputTwoThetaRange.testAndSetMin(includePartialBins ? twoThetas.min
                                                        : twoThetas.max);
    inputTwoThetaRange.testAndSetMax(includePartialBins ? twoThetas.max
                                                        : twoThetas.min);
    const auto &edges = detectorWS.binEdges(i);
    for (size_t xIndex = 0; xIndex < edges.size(); ++xIndex) {
      // It is common for the wavelength to have negative values at ILL.
      const auto x = edges[xIndex + (includePartialBins ? 0 : 1)];
      if (x > 0.) {
        inputLambdaRange.testAndSet(x);
        break;
      }
    }
    if (includePartialBins) {
      inputLambdaRange.testAndSet(edges.back());
    } else {
      inputLambdaRange.testAndSet(edges[edges.size() - 2]);
    }
  }

  MinMax outputLambdaRange;
  outputLambdaRange.min = projectToReference(inputLambdaRange.min,
                                             inputTwoThetaRange.max, refAngles);
  outputLambdaRange.max = projectToReference(inputLambdaRange.max,
                                             inputTwoThetaRange.min, refAngles);
  if (outputLambdaRange.min > outputLambdaRange.max) {
    throw std::runtime_error(
        "Error projecting lambda range to reference line; projected range (" +
        std::to_string(outputLambdaRange.min) + "," +
        std::to_string(outputLambdaRange.max) + ") is negative.");
  }
  return outputLambdaRange;
}
/** Calculate the derivatives of the newly smoothed data using the spline
 * Wraps CubicSpline derivative1D
 *
 * @param inputWorkspace :: The input workspace
 * @param outputWorkspace :: The output workspace
 * @param order :: The order of derivatives to calculate
 * @param row :: The row of spectra to use
 */
void SplineSmoothing::calculateDerivatives(
    const MatrixWorkspace &inputWorkspace,
    API::MatrixWorkspace &outputWorkspace, const int order,
    const size_t row) const {
    const auto &xIn = inputWorkspace.x(row);
    const double *xValues = &(xIn[0]);
    double *yValues = &(outputWorkspace.mutableY(order - 1)[0]);
    const size_t nData = xIn.size();

    m_cspline->derivative1D(yValues, xValues, nData, order);
}
Beispiel #6
0
/** The procedure analyses emode and efixed properties provided to the algorithm
 *and identify the energy analysis mode and the way the properties are defined
 *@param workspace     :: input workspace which may or may not have incident
 *energy property (Ei) attached to it as the run log
 *@param hostAlgorithm :: the pointer to SofQ algorithm hosting the base class.
 *This algorithm expects to have EMode and EFixed properties attached to it.
*/
void SofQCommon::initCachedValues(const API::MatrixWorkspace &workspace,
                                  API::Algorithm *const hostAlgorithm) {
  // Retrieve the emode & efixed properties
  const std::string emode = hostAlgorithm->getProperty("EMode");
  // Convert back to an integer representation
  m_emode = 0;
  if (emode == "Direct")
    m_emode = 1;
  else if (emode == "Indirect")
    m_emode = 2;
  m_efixed = hostAlgorithm->getProperty("EFixed");

  // Check whether they should have supplied an EFixed value
  if (m_emode == 1) // Direct
  {
    // If GetEi was run then it will have been stored in the workspace, if not
    // the user will need to enter one
    if (m_efixed == 0.0) {
      if (workspace.run().hasProperty("Ei")) {
        Kernel::Property *p = workspace.run().getProperty("Ei");
        Kernel::PropertyWithValue<double> *eiProp =
            dynamic_cast<Kernel::PropertyWithValue<double> *>(p);
        if (!eiProp)
          throw std::runtime_error("Input workspace contains Ei but its "
                                   "property type is not a double.");
        m_efixed = (*eiProp)();
      } else {
        throw std::invalid_argument("Input workspace does not contain an "
                                    "EFixed value. Please provide one or run "
                                    "GetEi.");
      }
    } else {
      m_efixedGiven = true;
    }
  } else {
    if (m_efixed != 0.0) {
      m_efixedGiven = true;
    }
  }
}
Beispiel #7
0
/* Convert spectra mask into det-id mask using workspace as source of
*spectra-detector maps
*
* @param sourceWS       -- the workspace containing source spectra-detector map
*                          to use on masks
* @param maskedSpecID   -- vector of spectra id to mask
* @param singleDetIds   -- output vector of detector ids to mask
*/
void LoadMask::convertSpMasksToDetIDs(const API::MatrixWorkspace &sourceWS,
                                      const std::vector<int32_t> &maskedSpecID,
                                      std::vector<int32_t> &singleDetIds) {

  spec2index_map s2imap = sourceWS.getSpectrumToWorkspaceIndexMap();
  detid2index_map sourceDetMap =
      sourceWS.getDetectorIDToWorkspaceIndexMap(false);

  std::multimap<size_t, Mantid::detid_t> spectr2index_map;
  for (auto it = sourceDetMap.begin(); it != sourceDetMap.end(); it++) {
    spectr2index_map.insert(
        std::pair<size_t, Mantid::detid_t>(it->second, it->first));
  }
  spec2index_map new_map;
  for (size_t i = 0; i < maskedSpecID.size(); i++) {
    // find spectra number from spectra ID for the source workspace
    const auto itSpec = s2imap.find(maskedSpecID[i]);
    if (itSpec == s2imap.end()) {
      throw std::runtime_error(
          "Can not find spectra with ID: " +
          boost::lexical_cast<std::string>(maskedSpecID[i]) +
          " in the workspace" + sourceWS.getName());
    }
    size_t specN = itSpec->second;

    // find detector range related to this spectra id in the source workspace
    const auto source_range = spectr2index_map.equal_range(specN);
    if (source_range.first == spectr2index_map.end()) {
      throw std::runtime_error("Can not find spectra N: " +
                               boost::lexical_cast<std::string>(specN) +
                               " in the workspace" + sourceWS.getName());
    }
    // add detectors to the masked det-id list
    for (auto it = source_range.first; it != source_range.second; ++it) {
      singleDetIds.push_back(it->second);
    }
  }
}
Beispiel #8
0
/**
 * Construct an "empty" output workspace in virtual-lambda for summation in Q.
 *
 * @param detectorWS [in] :: the input workspace
 * @param indices [in] :: the workspace indices of the foreground histograms
 * @param refAngles [in] :: the reference angles
 * @return :: a 1D workspace where y values are all zero
 */
API::MatrixWorkspace_sptr ReflectometrySumInQ::constructIvsLamWS(
    const API::MatrixWorkspace &detectorWS,
    const Indexing::SpectrumIndexSet &indices, const Angles &refAngles) {

  // Calculate the number of bins based on the min/max wavelength, using
  // the same bin width as the input workspace
  const auto &edges = detectorWS.binEdges(refAngles.referenceWSIndex);
  const double binWidth =
      (edges.back() - edges.front()) / static_cast<double>(edges.size());
  const auto wavelengthRange =
      findWavelengthMinMax(detectorWS, indices, refAngles);
  if (std::abs(wavelengthRange.max - wavelengthRange.min) < binWidth) {
    throw std::runtime_error("Given wavelength range too small.");
  }
  const int numBins = static_cast<int>(
      std::ceil((wavelengthRange.max - wavelengthRange.min) / binWidth));
  // Construct the histogram with these X values. Y and E values are zero.
  const HistogramData::BinEdges bins(
      numBins + 1,
      HistogramData::LinearGenerator(wavelengthRange.min, binWidth));
  const HistogramData::Counts counts(numBins, 0.);
  const HistogramData::Histogram modelHistogram(std::move(bins),
                                                std::move(counts));
  // Create the output workspace
  API::MatrixWorkspace_sptr outputWS =
      DataObjects::create<DataObjects::Workspace2D>(detectorWS, 1,
                                                    std::move(modelHistogram));

  // Set the detector IDs and specturm number from the twoThetaR detector.
  const auto &thetaSpec = detectorWS.getSpectrum(refAngles.referenceWSIndex);
  auto &outSpec = outputWS->getSpectrum(0);
  outSpec.clearDetectorIDs();
  outSpec.addDetectorIDs(thetaSpec.getDetectorIDs());
  outSpec.setSpectrumNo(thetaSpec.getSpectrumNo());

  return outputWS;
}
Beispiel #9
0
/**
 * Linearly interpolate between the points in integrFlux at xValues and save the
 * results in yValues.
 * @param xValues :: X-values at which to interpolate
 * @param integrFlux :: A workspace with the spectra to interpolate
 * @param sp :: A workspace index for a spectrum in integrFlux to interpolate.
 * @param yValues :: A vector to save the results.
 */
void MDNormSCD::calcIntegralsForIntersections(
    const std::vector<double> &xValues, const API::MatrixWorkspace &integrFlux,
    size_t sp, std::vector<double> &yValues) const {
  assert(xValues.size() == yValues.size());

  // the x-data from the workspace
  const auto &xData = integrFlux.readX(sp);
  const double xStart = xData.front();
  const double xEnd = xData.back();

  // the values in integrFlux are expected to be integrals of a non-negative
  // function
  // ie they must make a non-decreasing function
  const auto &yData = integrFlux.readY(sp);
  size_t spSize = yData.size();

  const double yMin = 0.0;
  const double yMax = yData.back();

  size_t nData = xValues.size();
  // all integrals below xStart must be 0
  if (xValues[nData - 1] < xStart) {
    std::fill(yValues.begin(), yValues.end(), yMin);
    return;
  }

  // all integrals above xEnd must be equal tp yMax
  if (xValues[0] > xEnd) {
    std::fill(yValues.begin(), yValues.end(), yMax);
    return;
  }

  size_t i = 0;
  // integrals below xStart must be 0
  while (i < nData - 1 && xValues[i] < xStart) {
    yValues[i] = yMin;
    i++;
  }
  size_t j = 0;
  for (; i < nData; i++) {
    // integrals above xEnd must be equal tp yMax
    if (j >= spSize - 1) {
      yValues[i] = yMax;
    } else {
      double xi = xValues[i];
      while (j < spSize - 1 && xi > xData[j])
        j++;
      // if x falls onto an interpolation point return the corresponding y
      if (xi == xData[j]) {
        yValues[i] = yData[j];
      } else if (j == spSize - 1) {
        // if we get above xEnd it's yMax
        yValues[i] = yMax;
      } else if (j > 0) {
        // interpolate between the consecutive points
        double x0 = xData[j - 1];
        double x1 = xData[j];
        double y0 = yData[j - 1];
        double y1 = yData[j];
        yValues[i] = y0 + (y1 - y0) * (xi - x0) / (x1 - x0);
      } else // j == 0
      {
        yValues[i] = yMin;
      }
    }
  }
}
Beispiel #10
0
/**
 *  Finds the median of values in single bin histograms rejecting spectra from
 * masked
 *  detectors and the results of divide by zero (infinite and NaN).
 * The median is an average that is less affected by small numbers of very large
 * values.
 * @param input :: A histogram workspace with one entry in each bin
 * @param excludeZeroes :: If true then zeroes will not be included in the
 * median calculation
 * @param indexmap :: indexmap
 * @return The median value of the histograms in the workspace that was passed
 * to it
 * @throw out_of_range if a value is negative
 */
std::vector<double> DetectorDiagnostic::calculateMedian(
    const API::MatrixWorkspace &input, bool excludeZeroes,
    const std::vector<std::vector<size_t>> &indexmap) {
  std::vector<double> medianvec;
  g_log.debug("Calculating the median count rate of the spectra");

  bool checkForMask = false;
  Geometry::Instrument_const_sptr instrument = input.getInstrument();
  if (instrument != nullptr) {
    checkForMask = ((instrument->getSource() != nullptr) &&
                    (instrument->getSample() != nullptr));
  }
  const auto &spectrumInfo = input.spectrumInfo();

  for (const auto &hists : indexmap) {
    std::vector<double> medianInput;
    const int nhists = static_cast<int>(hists.size());
    // The maximum possible length is that of workspace length
    medianInput.reserve(nhists);

    PARALLEL_FOR_IF(Kernel::threadSafe(input))
    for (int i = 0; i < nhists; ++i) { // NOLINT
      PARALLEL_START_INTERUPT_REGION

      if (checkForMask && spectrumInfo.hasDetectors(hists[i])) {
        if (spectrumInfo.isMasked(hists[i]) || spectrumInfo.isMonitor(hists[i]))
          continue;
      }

      const double yValue = input.readY(hists[i])[0];
      if (yValue < 0.0) {
        throw std::out_of_range("Negative number of counts found, could be "
                                "corrupted raw counts or solid angle data");
      }
      if (!std::isfinite(yValue) ||
          (excludeZeroes && yValue < DBL_EPSILON)) // NaNs/Infs
      {
        continue;
      }
      // Now we have a good value
      PARALLEL_CRITICAL(DetectorDiagnostic_median_d) {
        medianInput.push_back(yValue);
      }

      PARALLEL_END_INTERUPT_REGION
    }
    PARALLEL_CHECK_INTERUPT_REGION

    if (medianInput.empty()) {
      g_log.information(
          "some group has no valid histograms. Will use 0 for median.");
      medianInput.push_back(0.);
    }

    Kernel::Statistics stats =
        Kernel::getStatistics(medianInput, StatOptions::Median);
    double median = stats.median;

    if (median < 0 || median > DBL_MAX / 10.0) {
      throw std::out_of_range("The calculated value for the median was either "
                              "negative or unreliably large");
    }
    medianvec.push_back(median);
  }
  return medianvec;
}
Beispiel #11
0
std::vector<std::vector<size_t>>
DetectorDiagnostic::makeInstrumentMap(const API::MatrixWorkspace &countsWS) {
  return {
      {boost::counting_iterator<std::size_t>(0),
       boost::counting_iterator<std::size_t>(countsWS.getNumberHistograms())}};
}