/** Calculate the interpolation of the input points against the spline * * @param inputWorkspace :: The input workspace * @param outputWorkspace :: The output workspace * @param row :: The row of spectra to use */ void SplineInterpolation::calculateSpline( MatrixWorkspace_const_sptr inputWorkspace, MatrixWorkspace_sptr outputWorkspace, int row) const { // setup input parameters const size_t nData = inputWorkspace->y(0).size(); const double *xValues = &(inputWorkspace->x(0)[0]); double *yValues = &(outputWorkspace->mutableY(row)[0]); // calculate the interpolation m_cspline->function1D(yValues, xValues, nData); }
/** Sets the points defining the spline * * @param inputWorkspace :: The input workspace containing the points of the *spline * @param row :: The row of spectra to use */ void SplineInterpolation::setInterpolationPoints( MatrixWorkspace_const_sptr inputWorkspace, const int row) const { const auto &xIn = inputWorkspace->x(row); const auto &yIn = inputWorkspace->y(row); int size = static_cast<int>(xIn.size()); // pass x attributes and y parameters to CubicSpline m_cspline->setAttributeValue("n", size); for (int i = 0; i < size; ++i) { m_cspline->setXAttribute(i, xIn[i]); m_cspline->setParameter(i, yIn[i]); } }
/** * Add -1.*minValue on each spectra. * * @param minWS A workspace of minimum values for each spectra. This is *calculated in * the @see exec portion of the algorithm. * @param wksp The workspace to modify. * @param prog The progress. */ void ResetNegatives::pushMinimum(MatrixWorkspace_const_sptr minWS, MatrixWorkspace_sptr wksp, Progress &prog) { int64_t nHist = minWS->getNumberHistograms(); PARALLEL_FOR_IF(Kernel::threadSafe(*wksp, *minWS)) for (int64_t i = 0; i < nHist; i++) { PARALLEL_START_INTERUPT_REGION double minValue = minWS->y(i)[0]; if (minValue <= 0) { minValue *= -1.; auto &y = wksp->mutableY(i); for (double &value : y) { value = fixZero(value + minValue); } } prog.report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION }
/** * Search each spectrum for y-values that are less than zero and reset * them to the supplied value. * * @param minWS A workspace of minimum values for each spectra. * @param spectrumNegativeValues Reset negative values in the spectra to this * number. * @param wksp The workspace to modify. * @param prog The progress. */ void ResetNegatives::changeNegatives(MatrixWorkspace_const_sptr minWS, const double spectrumNegativeValues, MatrixWorkspace_sptr wksp, Progress &prog) { int64_t nHist = wksp->getNumberHistograms(); PARALLEL_FOR_IF(Kernel::threadSafe(*minWS, *wksp)) for (int64_t i = 0; i < nHist; i++) { PARALLEL_START_INTERUPT_REGION if (minWS->y(i)[0] <= 0.) // quick check to see if there is a reason to bother { auto &y = wksp->mutableY(i); for (double &value : y) { if (value < 0.) { value = spectrumNegativeValues; } else value = fixZero(value); } } prog.report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION }
void SofQWCentre::exec() { using namespace Geometry; MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace"); // Do the full check for common binning if (!WorkspaceHelpers::commonBoundaries(*inputWorkspace)) { g_log.error( "The input workspace must have common binning across all spectra"); throw std::invalid_argument( "The input workspace must have common binning across all spectra"); } std::vector<double> verticalAxis; MatrixWorkspace_sptr outputWorkspace = setUpOutputWorkspace( inputWorkspace, getProperty("QAxisBinning"), verticalAxis); setProperty("OutputWorkspace", outputWorkspace); // Holds the spectrum-detector mapping std::vector<specnum_t> specNumberMapping; std::vector<detid_t> detIDMapping; m_EmodeProperties.initCachedValues(*inputWorkspace, this); int emode = m_EmodeProperties.m_emode; // Get a pointer to the instrument contained in the workspace Instrument_const_sptr instrument = inputWorkspace->getInstrument(); // Get the distance between the source and the sample (assume in metres) IComponent_const_sptr source = instrument->getSource(); IComponent_const_sptr sample = instrument->getSample(); V3D beamDir = sample->getPos() - source->getPos(); beamDir.normalize(); try { double l1 = source->getDistance(*sample); g_log.debug() << "Source-sample distance: " << l1 << '\n'; } catch (Exception::NotFoundError &) { g_log.error("Unable to calculate source-sample distance"); throw Exception::InstrumentDefinitionError( "Unable to calculate source-sample distance", inputWorkspace->getTitle()); } // Conversion constant for E->k. k(A^-1) = sqrt(energyToK*E(meV)) const double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 / (PhysicalConstants::h * PhysicalConstants::h); // Loop over input workspace bins, reassigning data to correct bin in output // qw workspace const size_t numHists = inputWorkspace->getNumberHistograms(); const size_t numBins = inputWorkspace->blocksize(); Progress prog(this, 0.0, 1.0, numHists); for (int64_t i = 0; i < int64_t(numHists); ++i) { try { // Now get the detector object for this histogram IDetector_const_sptr spectrumDet = inputWorkspace->getDetector(i); if (spectrumDet->isMonitor()) continue; const double efixed = m_EmodeProperties.getEFixed(*spectrumDet); // For inelastic scattering the simple relationship q=4*pi*sinTheta/lambda // does not hold. In order to // be completely general we must calculate the momentum transfer by // calculating the incident and final // wave vectors and then use |q| = sqrt[(ki - kf)*(ki - kf)] DetectorGroup_const_sptr detGroup = boost::dynamic_pointer_cast<const DetectorGroup>(spectrumDet); std::vector<IDetector_const_sptr> detectors; if (detGroup) { detectors = detGroup->getDetectors(); } else { detectors.push_back(spectrumDet); } const size_t numDets = detectors.size(); // cache to reduce number of static casts const double numDets_d = static_cast<double>(numDets); const auto &Y = inputWorkspace->y(i); const auto &E = inputWorkspace->e(i); const auto &X = inputWorkspace->x(i); // Loop over the detectors and for each bin calculate Q for (size_t idet = 0; idet < numDets; ++idet) { IDetector_const_sptr det = detectors[idet]; // Calculate kf vector direction and then Q for each energy bin V3D scatterDir = (det->getPos() - sample->getPos()); scatterDir.normalize(); for (size_t j = 0; j < numBins; ++j) { const double deltaE = 0.5 * (X[j] + X[j + 1]); // Compute ki and kf wave vectors and therefore q = ki - kf double ei(0.0), ef(0.0); if (emode == 1) { ei = efixed; ef = efixed - deltaE; if (ef < 0) { std::string mess = "Energy transfer requested in Direct mode exceeds incident " "energy.\n Found for det ID: " + std::to_string(idet) + " bin No " + std::to_string(j) + " with Ei=" + boost::lexical_cast<std::string>(efixed) + " and energy transfer: " + boost::lexical_cast<std::string>(deltaE); throw std::runtime_error(mess); } } else { ei = efixed + deltaE; ef = efixed; if (ef < 0) { std::string mess = "Incident energy of a neutron is negative. Are you trying to " "process Direct data in Indirect mode?\n Found for det ID: " + std::to_string(idet) + " bin No " + std::to_string(j) + " with efied=" + boost::lexical_cast<std::string>(efixed) + " and energy transfer: " + boost::lexical_cast<std::string>(deltaE); throw std::runtime_error(mess); } } if (ei < 0) throw std::runtime_error( "Negative incident energy. Check binning."); const V3D ki = beamDir * sqrt(energyToK * ei); const V3D kf = scatterDir * (sqrt(energyToK * (ef))); const double q = (ki - kf).norm(); // Test whether it's in range of the Q axis if (q < verticalAxis.front() || q > verticalAxis.back()) continue; // Find which q bin this point lies in const MantidVec::difference_type qIndex = std::upper_bound(verticalAxis.begin(), verticalAxis.end(), q) - verticalAxis.begin() - 1; // Add this spectra-detector pair to the mapping specNumberMapping.push_back( outputWorkspace->getSpectrum(qIndex).getSpectrumNo()); detIDMapping.push_back(det->getID()); // And add the data and it's error to that bin, taking into account // the number of detectors contributing to this bin outputWorkspace->mutableY(qIndex)[j] += Y[j] / numDets_d; // Standard error on the average outputWorkspace->mutableE(qIndex)[j] = sqrt((pow(outputWorkspace->e(qIndex)[j], 2) + pow(E[j], 2)) / numDets_d); } } } catch (Exception::NotFoundError &) { // Get to here if exception thrown when calculating distance to detector // Presumably, if we get to here the spectrum will be all zeroes anyway // (from conversion to E) continue; } prog.report(); } // If the input workspace was a distribution, need to divide by q bin width if (inputWorkspace->isDistribution()) this->makeDistribution(outputWorkspace, verticalAxis); // Set the output spectrum-detector mapping SpectrumDetectorMapping outputDetectorMap(specNumberMapping, detIDMapping); outputWorkspace->updateSpectraUsing(outputDetectorMap); // Replace any NaNs in outputWorkspace with zeroes if (this->getProperty("ReplaceNaNs")) { auto replaceNans = this->createChildAlgorithm("ReplaceSpecialValues"); replaceNans->setChild(true); replaceNans->initialize(); replaceNans->setProperty("InputWorkspace", outputWorkspace); replaceNans->setProperty("OutputWorkspace", outputWorkspace); replaceNans->setProperty("NaNValue", 0.0); replaceNans->setProperty("InfinityValue", 0.0); replaceNans->setProperty("BigNumberThreshold", DBL_MAX); replaceNans->execute(); } }