/** Creates a vector of IndexCandidatePair-objects. * * This function iterates through all peaks in the measured *PoldiPeakCollection, getting possible indexing candidates * for each peak (see PoldiIndexKnownCompounds::getIndexCandidatePairs). If no *candidates are found it means that * there are no reflections with similar d-spacings from the known compounds, *so it is treated as an unindexed peak. * Otherwise, all candidates for this peak are appended to the list of existing *candidate pairs. * * @param measured :: Measured peaks. * @param knownCompoundPeaks :: Collections of expected peaks. * @return Vector of index candidates. */ std::vector<IndexCandidatePair> PoldiIndexKnownCompounds::getAllIndexCandidatePairs( const PoldiPeakCollection_sptr &measured, const std::vector<PoldiPeakCollection_sptr> &knownCompoundPeaks) { std::vector<IndexCandidatePair> candidates; size_t peakCount = measured->peakCount(); for (size_t i = 0; i < peakCount; ++i) { PoldiPeak_sptr currentPeak = measured->peak(i); std::vector<IndexCandidatePair> currentCandidates = getIndexCandidatePairs(currentPeak, knownCompoundPeaks); if (currentCandidates.empty()) { collectUnindexedPeak(currentPeak); } else { candidates.insert(candidates.end(), currentCandidates.begin(), currentCandidates.end()); } g_log.information() << " Peak at d=" << static_cast<double>(currentPeak->d()) << " has " << currentCandidates.size() << " candidates." << std::endl; } return candidates; }
/** * Returns a Poldi2DFunction that encapsulates individual peaks * * This function takes all peaks from the supplied peak collection and * generates an IPeakFunction of the type given in the name parameter, wraps * them in a Poldi2DFunction and returns it. * * @param profileFunctionName :: Profile function name. * @param peakCollection :: Peak collection with peaks to be used in the fit. * @return :: A Poldi2DFunction with peak profile functions. */ Poldi2DFunction_sptr PoldiFitPeaks2D::getFunctionIndividualPeaks( std::string profileFunctionName, const PoldiPeakCollection_sptr &peakCollection) const { auto mdFunction = boost::make_shared<Poldi2DFunction>(); for (size_t i = 0; i < peakCollection->peakCount(); ++i) { PoldiPeak_sptr peak = peakCollection->peak(i); boost::shared_ptr<PoldiSpectrumDomainFunction> peakFunction = boost::dynamic_pointer_cast<PoldiSpectrumDomainFunction>( FunctionFactory::Instance().createFunction( "PoldiSpectrumDomainFunction")); if (!peakFunction) { throw std::invalid_argument( "Cannot process null pointer poldi function."); } peakFunction->setDecoratedFunction(profileFunctionName); IPeakFunction_sptr wrappedProfile = boost::dynamic_pointer_cast<IPeakFunction>( peakFunction->getProfileFunction()); if (wrappedProfile) { wrappedProfile->setCentre(peak->d()); wrappedProfile->setFwhm(peak->fwhm(PoldiPeak::AbsoluteD)); wrappedProfile->setIntensity(peak->intensity()); } mdFunction->addFunction(peakFunction); } return mdFunction; }
/** * Converts normalized peak intensities to count based integral intensities * * This operation is the opposite of getNormalizedPeakCollection and is used * to convert the intensities back to integral intensities. * * @param peakCollection :: PoldiPeakCollection with normalized intensities * @return PoldiPeakCollection with integral intensities */ PoldiPeakCollection_sptr PoldiFitPeaks2D::getCountPeakCollection( const PoldiPeakCollection_sptr &peakCollection) const { if (!peakCollection) { throw std::invalid_argument( "Cannot proceed with invalid PoldiPeakCollection."); } if (!m_timeTransformer) { throw std::invalid_argument("Cannot proceed without PoldiTimeTransformer."); } PoldiPeakCollection_sptr countPeakCollection = boost::make_shared<PoldiPeakCollection>(PoldiPeakCollection::Integral); countPeakCollection->setProfileFunctionName( peakCollection->getProfileFunctionName()); // Get crystal data into new peak collection assignCrystalData(countPeakCollection, peakCollection); for (size_t i = 0; i < peakCollection->peakCount(); ++i) { PoldiPeak_sptr peak = peakCollection->peak(i); double calculatedIntensity = m_timeTransformer->calculatedTotalIntensity(peak->d()); PoldiPeak_sptr countPeak = peak->clone(); countPeak->setIntensity(peak->intensity() * calculatedIntensity); countPeakCollection->addPeak(countPeak); } return countPeakCollection; }
/// Assign Miller indices from one peak collection to another. void PoldiFitPeaks2D::assignMillerIndices(const PoldiPeakCollection_sptr &from, PoldiPeakCollection_sptr &to) const { if (!from || !to) { throw std::invalid_argument("Cannot process invalid peak collections."); } if (from->peakCount() != to->peakCount()) { throw std::runtime_error( "Cannot assign indices if number of peaks does not match."); } for (size_t i = 0; i < from->peakCount(); ++i) { PoldiPeak_sptr fromPeak = from->peak(i); PoldiPeak_sptr toPeak = to->peak(i); toPeak->setHKL(fromPeak->hkl()); } }
std::vector<RefinedRange_sptr> PoldiFitPeaks1D2::getRefinedRanges( const PoldiPeakCollection_sptr &peaks) const { std::vector<RefinedRange_sptr> ranges; for (size_t i = 0; i < peaks->peakCount(); ++i) { ranges.push_back( boost::make_shared<RefinedRange>(peaks->peak(i), m_fwhmMultiples)); } return ranges; }
/** * Returns a Poldi2DFunction that encapsulates a PawleyFunction * * This function creates a PawleyFunction using the supplied profile function * name and the crystal system as well as initial cell from the input * properties of the algorithm and wraps it in a Poldi2DFunction. * * The cell is refined using LatticeFunction to get better starting values. * * Because the peak intensities are integral at this step but PawleyFunction * expects peak heights, a profile function is created and * setIntensity/height-methods are used to convert. * * @param profileFunctionName :: Profile function name for PawleyFunction. * @param peakCollection :: Peak collection with peaks to be used in the fit. * @return :: A Poldi2DFunction with a PawleyFunction. */ Poldi2DFunction_sptr PoldiFitPeaks2D::getFunctionPawley( std::string profileFunctionName, const PoldiPeakCollection_sptr &peakCollection) { auto mdFunction = boost::make_shared<Poldi2DFunction>(); boost::shared_ptr<PoldiSpectrumPawleyFunction> poldiPawleyFunction = boost::dynamic_pointer_cast<PoldiSpectrumPawleyFunction>( FunctionFactory::Instance().createFunction( "PoldiSpectrumPawleyFunction")); if (!poldiPawleyFunction) { throw std::invalid_argument("Could not create pawley function."); } poldiPawleyFunction->setDecoratedFunction("PawleyFunction"); IPawleyFunction_sptr pawleyFunction = poldiPawleyFunction->getPawleyFunction(); pawleyFunction->setProfileFunction(profileFunctionName); // Extract crystal system from peak collection PointGroup_sptr pointGroup = peakCollection->pointGroup(); if (!pointGroup) { throw std::invalid_argument("Can not initialize pawley function properly - " "peaks do not have point group."); } std::string latticeSystem = getLatticeSystemFromPointGroup(pointGroup); pawleyFunction->setLatticeSystem(latticeSystem); UnitCell cell = peakCollection->unitCell(); // Extract unit cell from peak collection pawleyFunction->setUnitCell(getRefinedStartingCell( unitCellToStr(cell), latticeSystem, peakCollection)); IPeakFunction_sptr pFun = boost::dynamic_pointer_cast<IPeakFunction>( FunctionFactory::Instance().createFunction(profileFunctionName)); for (size_t i = 0; i < peakCollection->peakCount(); ++i) { PoldiPeak_sptr peak = peakCollection->peak(i); pFun->setCentre(peak->d()); pFun->setFwhm(peak->fwhm(PoldiPeak::AbsoluteD)); pFun->setIntensity(peak->intensity()); pawleyFunction->addPeak(peak->hkl().asV3D(), peak->fwhm(PoldiPeak::AbsoluteD), pFun->height()); } pawleyFunction->fix(pawleyFunction->parameterIndex("f0.ZeroShift")); mdFunction->addFunction(poldiPawleyFunction); return mdFunction; }
/// Converts the given tolerance (interpreted as standard deviation of a normal /// probability distribution) to FWHM and assigns that to all peaks of the /// supplied collection. void PoldiIndexKnownCompounds::assignFwhmEstimates( const PoldiPeakCollection_sptr &peakCollection, double tolerance) const { if (!peakCollection) { throw std::invalid_argument( "Cannot assign intensities to invalid PoldiPeakCollection."); } size_t peakCount = peakCollection->peakCount(); double fwhm = sigmaToFwhm(tolerance); for (size_t i = 0; i < peakCount; ++i) { PoldiPeak_sptr peak = peakCollection->peak(i); peak->setFwhm(UncertainValue(fwhm), PoldiPeak::Relative); } }
/** Scales the intensities of all peaks in the collection by the supplied *scattering contribution * * This method scales intensities of peaks contained in the supplied *PoldiPeakCollection * (if it's a null-pointer, the method throws an std::invalid_argument *exception). The original * intensity is multiplied by the contribution factor. * * @param peakCollection :: PoldiPeakCollection with expected peaks of one *phase. * @param contribution :: Scattering contribution of that material. */ void PoldiIndexKnownCompounds::scaleIntensityEstimates( const PoldiPeakCollection_sptr &peakCollection, double contribution) const { if (!peakCollection) { throw std::invalid_argument( "Cannot assign intensities to invalid PoldiPeakCollection."); } size_t peakCount = peakCollection->peakCount(); for (size_t i = 0; i < peakCount; ++i) { PoldiPeak_sptr peak = peakCollection->peak(i); peak->setIntensity(peak->intensity() * contribution); } }
void PoldiFitPeaks1D2::exec() { setPeakFunction(getProperty("PeakFunction")); // Number of points around the peak center to use for the fit m_fwhmMultiples = getProperty("FwhmMultiples"); m_maxRelativeFwhm = getProperty("MaximumRelativeFwhm"); // try to construct PoldiPeakCollection from provided TableWorkspace TableWorkspace_sptr poldiPeakTable = getProperty("PoldiPeakTable"); m_peaks = getInitializedPeakCollection(poldiPeakTable); PoldiPeakCollection_sptr fittedPeaksNew = fitPeaks(m_peaks); PoldiPeakCollection_sptr fittedPeaksOld = m_peaks; int i = 0; while (fittedPeaksNew->peakCount() < fittedPeaksOld->peakCount() || i < 1) { fittedPeaksOld = fittedPeaksNew; fittedPeaksNew = fitPeaks(fittedPeaksOld); ++i; } setProperty("OutputWorkspace", fittedPeaksNew->asTableWorkspace()); setProperty("FitPlotsWorkspace", m_fitplots); }
TableWorkspace_sptr PoldiPeakSummary::getSummaryTable( const PoldiPeakCollection_sptr &peakCollection) const { if (!peakCollection) { throw std::invalid_argument( "Cannot create summary of a null PoldiPeakCollection."); } TableWorkspace_sptr peakResultWorkspace = getInitializedResultWorkspace(); for (size_t i = 0; i < peakCollection->peakCount(); ++i) { storePeakSummary(peakResultWorkspace->appendRow(), peakCollection->peak(i)); } return peakResultWorkspace; }
PoldiPeakCollection_sptr PoldiFitPeaks1D2::getReducedPeakCollection( const PoldiPeakCollection_sptr &peaks) const { PoldiPeakCollection_sptr reducedPeaks = boost::make_shared<PoldiPeakCollection>(); reducedPeaks->setProfileFunctionName(peaks->getProfileFunctionName()); for (size_t i = 0; i < peaks->peakCount(); ++i) { PoldiPeak_sptr currentPeak = peaks->peak(i); if (peakIsAcceptable(currentPeak)) { reducedPeaks->addPeak(currentPeak); } } return reducedPeaks; }
size_t PoldiIndexKnownCompounds::getMaximumIntensityPeakIndex( const PoldiPeakCollection_sptr &peakCollection) const { double maxInt = 0.0; size_t maxIndex = 0; for (size_t i = 0; i < peakCollection->peakCount(); ++i) { PoldiPeak_sptr currentPeak = peakCollection->peak(i); double currentInt = currentPeak->intensity(); if (currentInt > maxInt) { maxInt = currentInt; maxIndex = i; } } return maxIndex; }
/** * Tries to refine the initial cell using the supplied peaks * * This method tries to refine the initial unit cell using the indexed peaks * that are supplied in the PoldiPeakCollection. If there are unindexed peaks, * the cell will not be refined at all, instead the unmodified initial cell * is returned. * * @param initialCell :: String with the initial unit cell * @param crystalSystem :: Crystal system name * @param peakCollection :: Collection of bragg peaks, must be indexed * * @return String for refined unit cell */ std::string PoldiFitPeaks2D::getRefinedStartingCell( const std::string &initialCell, const std::string &latticeSystem, const PoldiPeakCollection_sptr &peakCollection) { Geometry::UnitCell cell = Geometry::strToUnitCell(initialCell); ILatticeFunction_sptr latticeFunction = boost::dynamic_pointer_cast<ILatticeFunction>( FunctionFactory::Instance().createFunction("LatticeFunction")); latticeFunction->setLatticeSystem(latticeSystem); latticeFunction->fix(latticeFunction->parameterIndex("ZeroShift")); latticeFunction->setUnitCell(cell); // Remove errors from d-values PoldiPeakCollection_sptr clone = peakCollection->clone(); for (size_t i = 0; i < clone->peakCount(); ++i) { PoldiPeak_sptr peak = clone->peak(i); // If there are unindexed peaks, don't refine, just return the initial cell if (peak->hkl() == MillerIndices()) { return initialCell; } peak->setD(UncertainValue(peak->d().value())); } TableWorkspace_sptr peakTable = clone->asTableWorkspace(); IAlgorithm_sptr fit = createChildAlgorithm("Fit"); fit->setProperty("Function", boost::static_pointer_cast<IFunction>(latticeFunction)); fit->setProperty("InputWorkspace", peakTable); fit->setProperty("CostFunction", "Unweighted least squares"); fit->execute(); Geometry::UnitCell refinedCell = latticeFunction->getUnitCell(); return Geometry::unitCellToStr(refinedCell); }
PoldiPeakCollection_sptr PoldiFitPeaks1D2::fitPeaks(const PoldiPeakCollection_sptr &peaks) { g_log.information() << "Peaks to fit: " << peaks->peakCount() << std::endl; std::vector<RefinedRange_sptr> rawRanges = getRefinedRanges(peaks); std::vector<RefinedRange_sptr> reducedRanges = getReducedRanges(rawRanges); g_log.information() << "Ranges used for fitting: " << reducedRanges.size() << std::endl; Workspace2D_sptr dataWorkspace = getProperty("InputWorkspace"); m_fitplots->removeAll(); for (size_t i = 0; i < reducedRanges.size(); ++i) { RefinedRange_sptr currentRange = reducedRanges[i]; int nMin = getBestChebyshevPolynomialDegree(dataWorkspace, currentRange); if (nMin > -1) { IAlgorithm_sptr fit = getFitAlgorithm(dataWorkspace, currentRange, nMin); fit->execute(); IFunction_sptr fitFunction = fit->getProperty("Function"); CompositeFunction_sptr composite = boost::dynamic_pointer_cast<CompositeFunction>(fitFunction); if (!composite) { throw std::runtime_error("Not a composite function!"); } std::vector<PoldiPeak_sptr> peaks = currentRange->getPeaks(); for (size_t i = 0; i < peaks.size(); ++i) { setValuesFromProfileFunction(peaks[i], composite->getFunction(i)); MatrixWorkspace_sptr fpg = fit->getProperty("OutputWorkspace"); m_fitplots->addWorkspace(fpg); } } } return getReducedPeakCollection(peaks); }
/// Uses PoldiIndexKnownCompounds::isCandidate to find all candidates for /// supplied peak in the candidate peak collections. std::vector<IndexCandidatePair> PoldiIndexKnownCompounds::getIndexCandidatePairs( const PoldiPeak_sptr &peak, const std::vector<PoldiPeakCollection_sptr> &candidateCollections) const { std::vector<IndexCandidatePair> indexCandidates; for (size_t i = 0; i < candidateCollections.size(); ++i) { PoldiPeakCollection_sptr currentCandidateCollection = candidateCollections[i]; size_t peakCount = currentCandidateCollection->peakCount(); for (size_t p = 0; p < peakCount; ++p) { PoldiPeak_sptr currentCandidate = currentCandidateCollection->peak(p); if (isCandidate(peak, currentCandidate)) { indexCandidates.push_back( IndexCandidatePair(peak, currentCandidate, i)); } } } return indexCandidates; }
/** Execute the algorithm. */ void PoldiIndexKnownCompounds::exec() { g_log.information() << "Starting POLDI peak indexing." << std::endl; DataObjects::TableWorkspace_sptr peakTableWorkspace = getProperty("InputWorkspace"); PoldiPeakCollection_sptr unindexedPeaks = boost::make_shared<PoldiPeakCollection>(peakTableWorkspace); g_log.information() << " Number of peaks: " << unindexedPeaks->peakCount() << std::endl; std::vector<Workspace_sptr> workspaces = getWorkspaces(getProperty("CompoundWorkspaces")); std::vector<PoldiPeakCollection_sptr> peakCollections = getPeakCollections(workspaces); g_log.information() << " Number of phases: " << peakCollections.size() << std::endl; /* The procedure is much easier to formulate with some state stored in member * variables, * which are initialized either from user input or from some defaults. */ setMeasuredPeaks(unindexedPeaks); setExpectedPhases(peakCollections); setExpectedPhaseNames(getWorkspaceNames(workspaces)); initializeUnindexedPeaks(); initializeIndexedPeaks(m_expectedPhases); /* For calculating scores in the indexing procedure, scattering contributions * are used. * The structure factors are scaled accordingly. */ std::vector<double> contributions = getContributions(m_expectedPhases.size()); std::vector<double> normalizedContributions = getNormalizedContributions(contributions); scaleIntensityEstimates(peakCollections, normalizedContributions); scaleToExperimentalValues(peakCollections, unindexedPeaks); // Tolerances on the other hand are handled as "FWHM". std::vector<double> tolerances = getTolerances(m_expectedPhases.size()); assignFwhmEstimates(peakCollections, tolerances); // With all necessary state assigned, the indexing procedure can be executed indexPeaks(unindexedPeaks, peakCollections); g_log.information() << " Unindexed peaks: " << m_unindexedPeaks->peakCount() << std::endl; /* Finally, the peaks are put into separate workspaces, determined by * the phase they have been attributed to, plus unindexed peaks. */ std::string inputWorkspaceName = getPropertyValue("InputWorkspace"); WorkspaceGroup_sptr outputWorkspaces = boost::make_shared<WorkspaceGroup>(); for (size_t i = 0; i < m_indexedPeaks.size(); ++i) { PoldiPeakCollection_sptr intensitySorted = getIntensitySortedPeakCollection(m_indexedPeaks[i]); assignCrystalStructureParameters(intensitySorted, m_expectedPhases[i]); ITableWorkspace_sptr tableWs = intensitySorted->asTableWorkspace(); AnalysisDataService::Instance().addOrReplace( inputWorkspaceName + "_indexed_" + m_phaseNames[i], tableWs); outputWorkspaces->addWorkspace(tableWs); } ITableWorkspace_sptr unindexedTableWs = m_unindexedPeaks->asTableWorkspace(); AnalysisDataService::Instance().addOrReplace( inputWorkspaceName + "_unindexed", unindexedTableWs); outputWorkspaces->addWorkspace(unindexedTableWs); setProperty("OutputWorkspace", outputWorkspaces); }
/** * Return peak collection with integrated peaks * * This method takes a PoldiPeakCollection where the intensity is represented * by the maximum. Then it takes the profile function stored in the peak * collection, which must be the name of a registered * IPeakFunction-implementation. The parameters height and fwhm are assigned, * centre is set to 0 to avoid problems with the parameter transformation for * the integration from -inf to inf. The profiles are integrated using * a PeakFunctionIntegrator to the precision of 1e-10. * * The original peak collection is not modified, a new instance is created. * * @param rawPeakCollection :: PoldiPeakCollection * @return PoldiPeakCollection with integrated intensities */ PoldiPeakCollection_sptr PoldiFitPeaks2D::getIntegratedPeakCollection( const PoldiPeakCollection_sptr &rawPeakCollection) const { if (!rawPeakCollection) { throw std::invalid_argument( "Cannot proceed with invalid PoldiPeakCollection."); } if (!isValidDeltaT(m_deltaT)) { throw std::invalid_argument("Cannot proceed with invalid time bin size."); } if (!m_timeTransformer) { throw std::invalid_argument( "Cannot proceed with invalid PoldiTimeTransformer."); } if (rawPeakCollection->intensityType() == PoldiPeakCollection::Integral) { /* Intensities are integral already - don't need to do anything, * except cloning the collection, to make behavior consistent, since * integrating also results in a new peak collection. */ return rawPeakCollection->clone(); } /* If no profile function is specified, it's not possible to get integrated * intensities at all and we try to use the one specified by the user * instead. */ std::string profileFunctionName = rawPeakCollection->getProfileFunctionName(); if (!rawPeakCollection->hasProfileFunctionName()) { profileFunctionName = getPropertyValue("PeakProfileFunction"); } std::vector<std::string> allowedProfiles = FunctionFactory::Instance().getFunctionNames<IPeakFunction>(); if (std::find(allowedProfiles.begin(), allowedProfiles.end(), profileFunctionName) == allowedProfiles.end()) { throw std::runtime_error( "Cannot integrate peak profiles with invalid profile function."); } PoldiPeakCollection_sptr integratedPeakCollection = boost::make_shared<PoldiPeakCollection>(PoldiPeakCollection::Integral); integratedPeakCollection->setProfileFunctionName(profileFunctionName); // Preserve unit cell, point group assignCrystalData(integratedPeakCollection, rawPeakCollection); for (size_t i = 0; i < rawPeakCollection->peakCount(); ++i) { PoldiPeak_sptr peak = rawPeakCollection->peak(i); IPeakFunction_sptr profileFunction = boost::dynamic_pointer_cast<IPeakFunction>( FunctionFactory::Instance().createFunction(profileFunctionName)); profileFunction->setHeight(peak->intensity()); profileFunction->setFwhm(peak->fwhm(PoldiPeak::AbsoluteD)); PoldiPeak_sptr integratedPeak = peak->clone(); integratedPeak->setIntensity(UncertainValue(profileFunction->intensity())); integratedPeakCollection->addPeak(integratedPeak); } return integratedPeakCollection; }