/** * 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; }
/** * 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; }
/** * 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; }