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