size_t PawleyFunction::calculateFunctionValues( const API::IPeakFunction_sptr &peak, const API::FunctionDomain1D &domain, API::FunctionValues &localValues) const { size_t domainSize = domain.size(); const double *domainBegin = domain.getPointerAt(0); const double *domainEnd = domain.getPointerAt(domainSize); double centre = peak->centre(); double dx = m_peakRadius * peak->fwhm(); auto lb = std::lower_bound(domainBegin, domainEnd, centre - dx); auto ub = std::upper_bound(lb, domainEnd, centre + dx); size_t n = std::distance(lb, ub); if (n == 0) { throw std::invalid_argument("Null-domain"); } FunctionDomain1DView localDomain(lb, n); localValues.reset(localDomain); peak->functionLocal(localValues.getPointerToCalculated(0), localDomain.getPointerAt(0), n); return std::distance(domainBegin, lb); }
/// Populates a spectrum with peaks of type given by peakShape argument. /// @param spectrum :: A composite function that is a collection of peaks. /// @param peakShape :: A shape of each peak as a name of an IPeakFunction. /// @param centresAndIntensities :: A FunctionValues object containing centres /// and intensities for the peaks. First nPeaks calculated values are the /// centres and the following nPeaks values are the intensities. /// @param xVec :: x-values of a tabulated width function. /// @param yVec :: y-values of a tabulated width function. /// @param fwhmVariation :: A variation in the peak width allowed in a fit. /// @param defaultFWHM :: A default value for the FWHM to use if xVec and yVec /// are empty. /// @param nRequiredPeaks :: A number of peaks required to be created. /// @param fixAllPeaks :: If true fix all peak parameters /// @return :: The number of peaks that will be actually fitted. size_t buildSpectrumFunction(API::CompositeFunction &spectrum, const std::string &peakShape, const API::FunctionValues ¢resAndIntensities, const std::vector<double> &xVec, const std::vector<double> &yVec, double fwhmVariation, double defaultFWHM, size_t nRequiredPeaks, bool fixAllPeaks) { if (xVec.size() != yVec.size()) { throw std::runtime_error("WidthX and WidthY must have the same size."); } auto nPeaks = calculateNPeaks(centresAndIntensities); auto maxNPeaks = calculateMaxNPeaks(nPeaks); if (nRequiredPeaks > maxNPeaks) { maxNPeaks = nRequiredPeaks; } for (size_t i = 0; i < maxNPeaks; ++i) { const bool isGood = i < nPeaks; const auto centre = isGood ? centresAndIntensities.getCalculated(i) : 0.0; const auto intensity = isGood ? centresAndIntensities.getCalculated(i + nPeaks) : 0.0; auto peak = createPeak(peakShape, centre, intensity, xVec, yVec, fwhmVariation, defaultFWHM, isGood, fixAllPeaks); spectrum.addFunction(peak); } return nPeaks; }
//---------------------------------------------------------------------------------------------- /// Extract values from domain and values objects to vectors. /// @param domain :: A domain with fitting data arguments. /// @param values :: A FunctionValues object with the fitting data. /// @param x :: A vector to store the domain values /// @param y :: A vector to store the fitting data values. void extractValues(const API::FunctionDomain1D &domain, const API::FunctionValues &values, std::vector<double> &x, std::vector<double> &y) { size_t n = domain.size(); double start = domain[0]; double end = domain[n - 1]; auto dBegin = domain.getPointerAt(0); auto startIter = std::lower_bound(dBegin, dBegin + n, start); auto istart = static_cast<size_t>(std::distance(dBegin, startIter)); if (istart == n) { x.clear(); y.clear(); return; } auto endIter = std::lower_bound(startIter, dBegin + n, end); auto iend = static_cast<size_t>(std::distance(dBegin, endIter)); if (iend <= istart) { x.clear(); y.clear(); return; } n = iend - istart; x.resize(n); y.resize(n); for (size_t i = istart; i < iend; ++i) { auto j = i - istart; x[j] = domain[i]; y[j] = values.getFitData(i); } }
/** * @param index Index value into functionValues array * @param signal Calculated signal value * @param functionValues [InOut] Final calculated values */ void ResolutionConvolvedCrossSection::storeCalculatedWithMutex( const size_t index, const double signal, API::FunctionValues &functionValues) const { std::lock_guard<std::mutex> lock(m_valuesMutex); functionValues.setCalculated(index, signal); }
/// Calculate the number of visible peaks. size_t calculateNPeaks(const API::FunctionValues ¢resAndIntensities) { return centresAndIntensities.size() / 2; }
/** * Create an output event workspace filled with data simulated with the fitting * function. * @param baseName :: The base name for the workspace * @param inputWorkspace :: The input workspace. * @param values :: The calculated values * @param outputWorkspacePropertyName :: The property name */ boost::shared_ptr<API::Workspace> FitMD::createEventOutputWorkspace( const std::string &baseName, const API::IMDEventWorkspace &inputWorkspace, const API::FunctionValues &values, const std::string &outputWorkspacePropertyName) { auto outputWS = MDEventFactory::CreateMDWorkspace(inputWorkspace.getNumDims(), "MDEvent"); // Add events // TODO: Generalize to ND (the current framework is a bit limiting) auto mdWS = boost::dynamic_pointer_cast< DataObjects::MDEventWorkspace<DataObjects::MDEvent<4>, 4>>(outputWS); if (!mdWS) { return boost::shared_ptr<API::Workspace>(); } // Bins extents and meta data for (size_t i = 0; i < 4; ++i) { boost::shared_ptr<const Geometry::IMDDimension> inputDim = inputWorkspace.getDimension(i); Geometry::MDHistoDimensionBuilder builder; builder.setName(inputDim->getName()); builder.setId(inputDim->getDimensionId()); builder.setUnits(inputDim->getUnits()); builder.setNumBins(inputDim->getNBins()); builder.setMin(inputDim->getMinimum()); builder.setMax(inputDim->getMaximum()); builder.setFrameName(inputDim->getMDFrame().name()); outputWS->addDimension(builder.create()); } // Run information outputWS->copyExperimentInfos(inputWorkspace); // Coordinates outputWS->setCoordinateSystem(inputWorkspace.getSpecialCoordinateSystem()); // Set sensible defaults for splitting behaviour BoxController_sptr bc = outputWS->getBoxController(); bc->setSplitInto(3); bc->setSplitThreshold(3000); outputWS->initialize(); outputWS->splitBox(); auto inputIter = inputWorkspace.createIterator(); size_t resultValueIndex(0); const float errorSq = 0.0; do { const size_t numEvents = inputIter->getNumEvents(); const float signal = static_cast<float>(values.getCalculated(resultValueIndex)); for (size_t i = 0; i < numEvents; ++i) { coord_t centers[4] = { inputIter->getInnerPosition(i, 0), inputIter->getInnerPosition(i, 1), inputIter->getInnerPosition(i, 2), inputIter->getInnerPosition(i, 3)}; mdWS->addEvent(MDEvent<4>(signal, errorSq, inputIter->getInnerRunIndex(i), inputIter->getInnerDetectorID(i), centers)); } ++resultValueIndex; } while (inputIter->next()); delete inputIter; // This splits up all the boxes according to split thresholds and sizes. auto threadScheduler = new Kernel::ThreadSchedulerFIFO(); Kernel::ThreadPool threadPool(threadScheduler); outputWS->splitAllIfNeeded(threadScheduler); threadPool.joinAll(); outputWS->refreshCache(); // Store it if (!outputWorkspacePropertyName.empty()) { declareProperty( new API::WorkspaceProperty<API::IMDEventWorkspace>( outputWorkspacePropertyName, "", Direction::Output), "Name of the output Workspace holding resulting simulated spectrum"); m_manager->setPropertyValue(outputWorkspacePropertyName, baseName + "Workspace"); m_manager->setProperty(outputWorkspacePropertyName, outputWS); } return outputWS; }