/** * Project an input pixel onto an arbitrary reference line at a reference angle. * The projection is done along lines of constant Q, which emanate from the * horizon angle at wavelength = 0. The top-left and bottom-right corners of * the pixel are projected, resulting in an output range in "virtual" lambda. * * For a description of this projection, see: * R. Cubitt, T. Saerbeck, R.A. Campbell, R. Barker, P. Gutfreund * J. Appl. Crystallogr., 48 (6) (2015) * * @param wavelengthRange [in] :: the bin edges of the input bin * @param twoThetaRange [in] :: the 2theta width of the pixel * @param refAngles [in] :: the reference angles * @return :: the projected wavelength range */ ReflectometrySumInQ::MinMax ReflectometrySumInQ::projectedLambdaRange(const MinMax &wavelengthRange, const MinMax &twoThetaRange, const Angles &refAngles) { // We cannot project pixels below the horizon angle if (twoThetaRange.min <= refAngles.horizon) { const auto twoTheta = (twoThetaRange.min + twoThetaRange.max) / 2.; throw std::runtime_error( "Cannot process twoTheta=" + std::to_string(twoTheta * Geometry::rad2deg) + " as it is below the horizon angle=" + std::to_string(refAngles.horizon * Geometry::rad2deg)); } // Calculate the projected wavelength range MinMax range; try { const double lambdaTop = projectToReference(wavelengthRange.max, twoThetaRange.min, refAngles); const double lambdaBot = projectToReference(wavelengthRange.min, twoThetaRange.max, refAngles); range.testAndSet(lambdaBot); range.testAndSet(lambdaTop); } catch (std::exception &ex) { const auto twoTheta = (twoThetaRange.min + twoThetaRange.max) / 2.; const auto lambda = (wavelengthRange.min + wavelengthRange.max) / 2.; throw std::runtime_error( "Failed to project (lambda, twoTheta) = (" + std::to_string(lambda) + "," + std::to_string(twoTheta * Geometry::rad2deg) + ") onto twoThetaR = " + std::to_string(refAngles.twoTheta) + ": " + ex.what()); } return range; }
/** * 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; }