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