/// Returns true if d-spacing of measured and candidate peak are less than three /// sigma (of the candidate) apart. bool PoldiIndexKnownCompounds::isCandidate( const PoldiPeak_sptr &measuredPeak, const PoldiPeak_sptr &possibleCandidate) const { if (!measuredPeak || !possibleCandidate) { throw std::invalid_argument("Cannot check null-peaks."); } return (fabs(static_cast<double>(measuredPeak->d()) - possibleCandidate->d()) / fwhmToSigma(possibleCandidate->fwhm(PoldiPeak::AbsoluteD))) < 3.0; }
/** * 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; }
/** 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; }
void PoldiPeakSummary::storePeakSummary(TableRow tableRow, const PoldiPeak_sptr &peak) const { UncertainValue q = peak->q(); UncertainValue d = peak->d(); tableRow << MillerIndicesIO::toString(peak->hkl()) << UncertainValueIO::toString(q) << UncertainValueIO::toString(d) << d.error() / d.value() * 1e3 << UncertainValueIO::toString(peak->fwhm(PoldiPeak::Relative) * 1e3) << UncertainValueIO::toString(peak->intensity()); }
/** * 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; }
/** * 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); }
/** Assigns most likely indices to measured peaks. * * This method takes a list of index candidate pairs and sorts it according to *their score, because * a high score corresponds to a high probability of the assignment being *correct. Then the function * takes the element with the highest score and inspects the *IndexCandidatePair. If the measured reflection * does not have an index yet, it checks whether the candidate index has *already been used. In that case, * the measured peak is stored in a buffer. Otherwise, the candidate presents *the best solution for the * measured peak (because the current pair has the highest score in the list of *remaining candidate pairs) and * the solution is accepted. This means that the measured as well as the *candidate peak are marked as "used" and * the measured peak is stored in the PoldiPeakCollection that is located at *the index of the current pair. * * Then the next element is checked and so on. Whenever a "next best" solution *for a measured peak in the mentioned * buffer is found, the peak is removed from that buffer. Once all candidate *pairs have been evaluated, the peaks * that are still in the buffer are considered unindexed and treated *accordingly. * * For a more complete explanation of the principle, please check the *documentation in the wiki. * * @param candidates :: Vector of possible index candidates. */ void PoldiIndexKnownCompounds::assignCandidates( const std::vector<IndexCandidatePair> &candidates) { // Make a copy since this is going to be modified std::vector<IndexCandidatePair> workCandidates = candidates; /* The vector is sorted by score (see comparison operator of PeakCandidate), * so the first element has the lowest score (lowest probability of being * a good guess). */ std::sort(workCandidates.begin(), workCandidates.end()); std::set<PoldiPeak_sptr> usedMeasuredPeaks; std::set<PoldiPeak_sptr> usedExpectedPeaks; std::set<PoldiPeak_sptr> unassignedMeasuredPeaks; /* The candidate at the back of the vector has the highest score, * so it's the candidate with the highest probability of being correct. * Consequently, the vector is iterated from end to beginning. */ for (auto it = workCandidates.rbegin(); it != workCandidates.rend(); ++it) { IndexCandidatePair currentCandidate = *it; PoldiPeak_sptr measuredPeak = currentCandidate.observed; PoldiPeak_sptr expectedPeak = currentCandidate.candidate; g_log.information() << " Candidate d=" << static_cast<double>(measuredPeak->d()) << " -> " << "Phase: " << currentCandidate.candidateCollectionIndex << " [" << MillerIndicesIO::toString(expectedPeak->hkl()) << "] (d=" << static_cast<double>(expectedPeak->d()) << "), " << "Score=(" << currentCandidate.positionMatch << "): "; /* If the peak has not been indexed yet, it is not stored in the set * that holds measured peaks that are already indexed, so the candidate * needs to be examined further. */ if (!inPeakSet(usedMeasuredPeaks, measuredPeak)) { /* If the theoretical reflection of this index-candidate has already been * assigned to a measured peak, the measured peak is inserted into * a second set where it is kept in case there is another candidate * for this measured peak. */ if (inPeakSet(usedExpectedPeaks, expectedPeak)) { unassignedMeasuredPeaks.insert(measuredPeak); g_log.information() << " Candidate rejected: Candidate has been already used." << std::endl; } else { /* Otherwise, the indexed candidate is accepted and the measured peak * is removed from the set of peaks that are waiting for another * solution. */ if (inPeakSet(unassignedMeasuredPeaks, measuredPeak)) { unassignedMeasuredPeaks.erase(measuredPeak); } usedExpectedPeaks.insert(expectedPeak); usedMeasuredPeaks.insert(measuredPeak); assignPeakIndex(currentCandidate); g_log.information() << " Candidate accepted." << std::endl; } } else { g_log.information() << " Candidate rejected: peak has already been indexed: [" << MillerIndicesIO::toString(measuredPeak->hkl()) << "]." << std::endl; } } /* All peaks that are still in this set at this point are not indexed and thus * inserted into the * peak collection that holds unindexed peaks. */ for (auto it = unassignedMeasuredPeaks.begin(); it != unassignedMeasuredPeaks.end(); ++it) { collectUnindexedPeak(*it); } }