Mantid
Loading...
Searching...
No Matches
CrystalFieldPeakUtils.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
13
14#include <algorithm>
15#include <cmath>
16
18
19using namespace CurveFitting;
20using namespace Kernel;
21using namespace API;
22
28double calculateWidth(double x, const std::vector<double> &xVec, const std::vector<double> &yVec) {
29 assert(xVec.size() == yVec.size());
30 auto upperIt = std::lower_bound(xVec.begin(), xVec.end(), x);
31 if (upperIt == xVec.end() || x < xVec.front()) {
32 return -1.0;
33 }
34 if (upperIt == xVec.begin()) {
35 return yVec.front();
36 }
37 double lowerX = *(upperIt - 1);
38 double upperX = *upperIt;
39 auto i = std::distance(xVec.begin(), upperIt) - 1;
40 return yVec[i] + (yVec[i + 1] - yVec[i]) / (upperX - lowerX) * (x - lowerX);
41}
42
47void setWidthConstraint(API::IPeakFunction &peak, double fwhm, double fwhmVariation) {
48 double upperBound = fwhm + fwhmVariation;
49 double lowerBound = fwhm - fwhmVariation;
50 bool fix = lowerBound == upperBound;
51 if (!fix) {
52 if (lowerBound < 0.0) {
53 lowerBound = 0.0;
54 }
55 if (lowerBound >= upperBound) {
56 lowerBound = upperBound / 2;
57 }
58 }
59 if (peak.name() == "Lorentzian") {
60 if (fix) {
61 peak.fixParameter("FWHM");
62 return;
63 }
64 peak.removeConstraint("FWHM");
65 auto constraint = std::make_unique<Constraints::BoundaryConstraint>(&peak, "FWHM", lowerBound, upperBound);
66 peak.addConstraint(std::move(constraint));
67 } else if (peak.name() == "Gaussian") {
68 if (fix) {
69 peak.fixParameter("Sigma");
70 return;
71 }
72 const double WIDTH_TO_SIGMA = 2.0 * sqrt(2.0 * M_LN2);
73 lowerBound /= WIDTH_TO_SIGMA;
74 upperBound /= WIDTH_TO_SIGMA;
75 peak.removeConstraint("Sigma");
76 auto constraint = std::make_unique<Constraints::BoundaryConstraint>(&peak, "Sigma", lowerBound, upperBound);
77 peak.addConstraint(std::move(constraint));
78 } else {
79 throw std::runtime_error("Cannot set constraint on width of " + peak.name());
80 }
81}
82
84size_t calculateNPeaks(const API::FunctionValues &centresAndIntensities) { return centresAndIntensities.size() / 2; }
85
87size_t calculateMaxNPeaks(size_t nPeaks) { return nPeaks + nPeaks / 2 + 1; }
88
92inline void ignorePeak(API::IPeakFunction &peak, double fwhm) {
93 peak.setHeight(0.0);
94 peak.fixAll(true);
95 peak.setFwhm(fwhm);
96}
97
107void setPeakWidth(API::IPeakFunction &peak, double centre, const std::vector<double> &xVec,
108 const std::vector<double> &yVec, double fwhmVariation, double defaultFWHM, bool useDefaultFWHM) {
109 if (useDefaultFWHM) {
110 peak.setFwhm(defaultFWHM);
111 } else {
112 auto fwhm = calculateWidth(centre, xVec, yVec);
113 if (fwhm > 0.0) {
114 peak.setFwhm(fwhm);
115 setWidthConstraint(peak, fwhm, fwhmVariation);
116 } else {
117 ignorePeak(peak, defaultFWHM);
118 }
119 }
120}
121
134API::IPeakFunction_sptr createPeak(const std::string &peakShape, double centre, double intensity,
135 const std::vector<double> &xVec, const std::vector<double> &yVec,
136 double fwhmVariation, double defaultFWHM, bool isGood, bool fixAllPeaks) {
137 auto fun = API::FunctionFactory::Instance().createFunction(peakShape);
138 auto peak = std::dynamic_pointer_cast<API::IPeakFunction>(fun);
139 if (!peak) {
140 throw std::runtime_error("A peak function is expected.");
141 }
142 bool useDefaultFWHM = xVec.empty();
143 const bool fixByDefault = true;
144 if (isGood) {
145 peak->setCentre(centre);
146 peak->setIntensity(intensity);
147 setPeakWidth(*peak, centre, xVec, yVec, fwhmVariation, defaultFWHM, useDefaultFWHM);
148 peak->fixCentre(fixByDefault);
149 peak->fixIntensity(fixByDefault);
150 } else {
151 ignorePeak(*peak, defaultFWHM);
152 }
153 if (fixAllPeaks) {
154 peak->fixAll(fixByDefault);
155 }
156 return peak;
157}
158
173size_t buildSpectrumFunction(API::CompositeFunction &spectrum, const std::string &peakShape,
174 const API::FunctionValues &centresAndIntensities, const std::vector<double> &xVec,
175 const std::vector<double> &yVec, double fwhmVariation, double defaultFWHM,
176 size_t nRequiredPeaks, bool fixAllPeaks) {
177 if (xVec.size() != yVec.size()) {
178 throw std::runtime_error("WidthX and WidthY must have the same size.");
179 }
180
181 auto nPeaks = calculateNPeaks(centresAndIntensities);
182 auto maxNPeaks = calculateMaxNPeaks(nPeaks);
183 if (nRequiredPeaks > maxNPeaks) {
184 maxNPeaks = nRequiredPeaks;
185 }
186 for (size_t i = 0; i < maxNPeaks; ++i) {
187 const bool isGood = i < nPeaks;
188 const auto centre = isGood ? centresAndIntensities.getCalculated(i) : 0.0;
189 const auto intensity = isGood ? centresAndIntensities.getCalculated(i + nPeaks) : 0.0;
190 auto peak = createPeak(peakShape, centre, intensity, xVec, yVec, fwhmVariation, defaultFWHM, isGood, fixAllPeaks);
191 spectrum.addFunction(peak);
192 }
193 return nPeaks;
194}
195
202void updatePeakWidth(API::IPeakFunction &peak, double centre, const std::vector<double> &xVec,
203 const std::vector<double> &yVec, double fwhmVariation) {
204 bool mustUpdateWidth = !xVec.empty();
205 if (mustUpdateWidth) {
206 auto fwhm = peak.fwhm();
207 auto expectedFwhm = calculateWidth(centre, xVec, yVec);
208 if (expectedFwhm <= 0.0) {
209 ignorePeak(peak, fwhm);
210 } else if (fabs(fwhm - expectedFwhm) > fwhmVariation) {
211 peak.setFwhm(expectedFwhm);
212 setWidthConstraint(peak, expectedFwhm, fwhmVariation);
213 }
214 }
215}
216
226void updatePeak(API::IPeakFunction &peak, double centre, double intensity, const std::vector<double> &xVec,
227 const std::vector<double> &yVec, double fwhmVariation, bool isGood, bool fixAllPeaks) {
228 const bool fixByDefault = true;
229 if (isGood) {
230 peak.unfixAllDefault();
231 peak.setCentre(centre);
232 peak.setIntensity(intensity);
233 updatePeakWidth(peak, centre, xVec, yVec, fwhmVariation);
234 peak.unfixIntensity();
235 peak.fixIntensity(fixByDefault);
236 peak.fixCentre(fixByDefault);
237 if (fixAllPeaks) {
238 peak.fixAll(fixByDefault);
239 }
240 } else {
241 peak.setHeight(0.0);
242 peak.fixIntensity(fixByDefault);
243 peak.fixCentre(fixByDefault);
244 }
245}
246
264size_t updateSpectrumFunction(API::CompositeFunction &spectrum, const std::string &peakShape,
265 const FunctionValues &centresAndIntensities, size_t iFirst,
266 const std::vector<double> &xVec, const std::vector<double> &yVec, double fwhmVariation,
267 double defaultFWHM, bool fixAllPeaks) {
268 size_t nGoodPeaks = calculateNPeaks(centresAndIntensities);
269 size_t maxNPeaks = calculateMaxNPeaks(nGoodPeaks);
270 size_t nFunctions = spectrum.nFunctions();
271
272 for (size_t i = 0; i < maxNPeaks; ++i) {
273 const bool isGood = i < nGoodPeaks;
274 auto centre = isGood ? centresAndIntensities.getCalculated(i) : 0.0;
275 auto intensity = isGood ? centresAndIntensities.getCalculated(i + nGoodPeaks) : 0.0;
276
277 if (i < nFunctions - iFirst) {
278 auto fun = spectrum.getFunction(i + iFirst);
279 auto &peak = dynamic_cast<API::IPeakFunction &>(*fun);
280 updatePeak(peak, centre, intensity, xVec, yVec, fwhmVariation, isGood, fixAllPeaks);
281 } else {
282 auto peakPtr =
283 createPeak(peakShape, centre, intensity, xVec, yVec, fwhmVariation, defaultFWHM, isGood, fixAllPeaks);
284 spectrum.addFunction(peakPtr);
285 }
286 }
287 // If there are any peaks above the maxNPeaks, ignore them
288 // but don't remove
289 const bool fixByDefault = true;
290 for (size_t i = maxNPeaks; i < nFunctions - iFirst; ++i) {
291 auto fun = spectrum.getFunction(i + iFirst);
292 auto &peak = dynamic_cast<API::IPeakFunction &>(*fun);
293 const auto fwhm = peak.fwhm();
294 peak.setHeight(0.0);
295 peak.setFwhm(fwhm);
296 peak.fixIntensity(fixByDefault);
297 peak.fixCentre(fixByDefault);
298 }
299 return nGoodPeaks;
300}
301
302} // namespace Mantid::CurveFitting::Functions::CrystalFieldUtils
#define fabs(x)
Definition: Matrix.cpp:22
A composite function is a function containing other functions.
virtual size_t addFunction(IFunction_sptr f)
Add a function at the back of the internal function list.
std::size_t nFunctions() const override
Number of functions.
IFunction_sptr getFunction(std::size_t i) const override
Returns the pointer to i-th function.
A class to store values calculated by a function.
size_t size() const
Return the number of values.
double getCalculated(size_t i) const
Get i-th calculated value.
virtual void setHeight(const double h)=0
Sets the parameters such that height == h.
virtual void setCentre(const double c)=0
Sets the parameters such that centre == c.
virtual void fixCentre(bool isDefault=false)
Fix a parameter or set up a tie such that value returned by centre() is constant during fitting.
virtual void removeConstraint(const std::string &parName)
Remove a constraint.
Definition: IFunction.cpp:403
void unfixAllDefault()
Free all parameters fixed by default.
Definition: IFunction.cpp:1547
void fixParameter(const std::string &name, bool isDefault=false)
Fix a parameter.
Definition: IFunction.cpp:1515
virtual std::string name() const =0
Returns the function's name.
void fixAll(bool isDefault=false)
Fix all parameters.
Definition: IFunction.cpp:1529
virtual void addConstraint(std::unique_ptr< IConstraint > ic)
Add a constraint to function.
Definition: IFunction.cpp:376
An interface to a peak function, which extend the interface of IFunctionWithLocation by adding method...
Definition: IPeakFunction.h:51
virtual void setIntensity(const double newIntensity)
Sets the integral intensity of the peak.
virtual double fwhm() const =0
Returns the peak FWHM.
virtual void setFwhm(const double w)=0
Sets the parameters such that FWHM = w.
virtual void fixIntensity(bool isDefault=false)
Fix a parameter or set up a tie such that value returned by intensity() is constant during fitting.
virtual void unfixIntensity()
Free the intensity parameter.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< IPeakFunction > IPeakFunction_sptr
size_t calculateMaxNPeaks(size_t nPeaks)
Calculate the maximum number of peaks a spectrum can have.
size_t updateSpectrumFunction(API::CompositeFunction &spectrum, const std::string &peakShape, const API::FunctionValues &centresAndIntensities, size_t iFirst, const std::vector< double > &xVec, const std::vector< double > &yVec, double fwhmVariation, double defaultFWHM, bool fixAllPeaks)
Update the peaks parameters after recalculationof the crystal field.
void ignorePeak(API::IPeakFunction &peak, double fwhm)
Set peak's properties such that it was invisible in the spectrum.
void setPeakWidth(API::IPeakFunction &peak, double centre, const std::vector< double > &xVec, const std::vector< double > &yVec, double fwhmVariation, double defaultFWHM, bool useDefaultFWHM)
Set the width of a peak.
size_t calculateNPeaks(const API::FunctionValues &centresAndIntensities)
Calculate the number of visible peaks.
API::IPeakFunction_sptr createPeak(const std::string &peakShape, double centre, double intensity, const std::vector< double > &xVec, const std::vector< double > &yVec, double fwhmVariation, double defaultFWHM, bool isGood, bool fixAllPeaks)
Create a single peak.
size_t buildSpectrumFunction(API::CompositeFunction &spectrum, const std::string &peakShape, const API::FunctionValues &centresAndIntensities, const std::vector< double > &xVec, const std::vector< double > &yVec, double fwhmVariation, double defaultFWHM, size_t nRequiredPeaks, bool fixAllPeaks)
Utility functions to help set up peak functions in a Crystal Field spectrum.
double calculateWidth(double x, const std::vector< double > &xVec, const std::vector< double > &yVec)
Calculate the width of a peak cenrted at x using an interpolated value of a function tabulated at xVe...
void setWidthConstraint(API::IPeakFunction &peak, double fwhm, double fwhmVariation)
Set a boundary constraint on the appropriate parameter of the peak.
void updatePeakWidth(API::IPeakFunction &peak, double centre, const std::vector< double > &xVec, const std::vector< double > &yVec, double fwhmVariation)
Update width of a peak.
void updatePeak(API::IPeakFunction &peak, double centre, double intensity, const std::vector< double > &xVec, const std::vector< double > &yVec, double fwhmVariation, bool isGood, bool fixAllPeaks)
Update a single peak.