Mantid
Loading...
Searching...
No Matches
LeBailFunction.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 +
12#include "MantidHistogramData/HistogramX.h"
13
14#include "MantidHistogramData/HistogramY.h"
15
16#include <sstream>
17#include <utility>
18
19#include <gsl/gsl_sf_erf.h>
20
21using namespace Mantid::API;
22using namespace Mantid::Kernel;
23using Mantid::HistogramData::HistogramY;
24
25using namespace std;
26
27const double NEG_DBL_MAX(-1. * DBL_MAX);
28
30namespace {
31const double PEAKRANGECONSTANT = 5.0;
32
33const string CHEBYSHEV_BACKGROUND("Chebyshev");
34const string POLYNOMIAL_BACKGROUND("Polynomial");
35const string FULLPROF_POLYNOMIAL_BACKGROUND("FullprofPolynomial");
36} // namespace
37
38// Get a reference to the logger
39Kernel::Logger g_log("LeBailFunction");
40
41//----------------------------------------------------------------------------------------------
44LeBailFunction::LeBailFunction(const std::string &peaktype) {
45 // Set initial values to some class variables
47 m_compsiteFunction = m_function;
48
49 m_numPeaks = 0;
50
51 m_isInputValue = false;
52 m_hasNewPeakValue = false;
53
54 // Peak type, validate and parameter name vectors
55 m_peakType = peaktype;
56 IFunction_sptr ifunc = FunctionFactory::Instance().createFunction(m_peakType);
57 if (!ifunc) {
58 stringstream errss;
59 errss << "Input peak type " << peaktype << " is not a recoganizable Mantid function.";
60 throw runtime_error(errss.str());
61 }
62 IPowderDiffPeakFunction_sptr peakfunc = std::dynamic_pointer_cast<IPowderDiffPeakFunction>(ifunc);
63 if (!peakfunc) {
64 stringstream errss;
65 errss << "Input peak type " << peaktype << " is not a IPowderDiffPeakFunction.";
66 throw runtime_error(errss.str());
67 }
68
69 m_peakParameterNameVec = peakfunc->getParameterNames();
72
73 // Peak parameter values
74 for (auto parname : m_peakParameterNameVec) {
75 m_functionParameters.emplace(parname, 0.0);
76 }
77
78 // Importing peak position tolerance
80 m_maxTOFPeakCentre = DBL_MAX;
81}
82
83//----------------------------------------------------------------------------------------------
86LeBailFunction::~LeBailFunction() = default;
87
88//----------------------------------------------------------------------------------------------
91API::IFunction_sptr LeBailFunction::getFunction() {
92 return m_compsiteFunction;
93 // return std::dynamic_pointer_cast<IFunction_sptr>(m_compsiteFunction);
94}
95
96//----------------------------------------------------------------------------------------------
104HistogramY LeBailFunction::function(const Mantid::HistogramData::HistogramX &xvalues, bool calpeaks,
105 bool calbkgd) const {
106
107 // Reset output elements to zero
108 std::vector<double> out(xvalues.size(), 0);
109
110 // Peaks
111 if (calpeaks) {
112 for (size_t ipk = 0; ipk < m_numPeaks; ++ipk) {
113 // Reset temporary vector for output
114 vector<double> temp(xvalues.size(), 0);
116 peak->function(temp, xvalues);
117 transform(out.begin(), out.end(), temp.begin(), out.begin(), ::plus<double>());
118 }
119 }
120
121 // Background if required
122 if (calbkgd) {
123 if (!m_background) {
124 throw runtime_error("Must define background first!");
125 }
126
127 FunctionDomain1DVector domain(xvalues);
128 FunctionValues values(domain);
129 g_log.information() << "Background function (in LeBailFunction): " << m_background->asString() << ".\n";
130 m_background->function(domain, values);
131 size_t numpts = out.size();
132 for (size_t i = 0; i < numpts; ++i)
133 out[i] += values[i];
134 }
135
136 return HistogramY(out);
137}
138
141HistogramY LeBailFunction::calPeak(size_t ipk, std::span<double const> const xvalues, size_t ySize) const {
142
143 if (ipk >= m_numPeaks) {
144 stringstream errss;
145 errss << "Try to calculate peak indexed " << ipk << ". But number of peaks = " << m_numPeaks;
146 g_log.error(errss.str());
147 throw runtime_error(errss.str());
148 }
149
150 std::vector<double> out(ySize, 0);
152 peak->function(out, xvalues);
153 return HistogramY(out);
154}
155
156//----------------------------------------------------------------------------------------------
160bool LeBailFunction::hasProfileParameter(const std::string &paramname) {
161 auto fiter = lower_bound(m_orderedProfileParameterNames.cbegin(), m_orderedProfileParameterNames.cend(), paramname);
162
163 bool found = true;
164 if (fiter == m_orderedProfileParameterNames.end()) {
165 // End of the vector
166 found = false;
167 } else {
168 // Middle of vector
169 string matchparname = *fiter;
170 if (matchparname != paramname)
171 found = false;
172 }
173
174 return found;
175}
176
177//----------------------------------------------------------------------------------------------
182bool LeBailFunction::isParameterValid(double maxfwhm) const {
183 // Re-calculate peak parameter if there is some modification
184 if (m_hasNewPeakValue) {
186 }
187
188 // Check whether each peak has valid value
189 bool arevalid = true;
190 for (size_t i = 0; i < m_numPeaks; ++i) {
192 bool isvalid = peak->isPhysical();
193 if (isvalid && maxfwhm >= 0)
194 isvalid = peak->fwhm() < maxfwhm;
195 if (!isvalid) {
196 arevalid = false;
197
198 int h, k, l;
199 peak->getMillerIndex(h, k, l);
200 g_log.information() << "Peak [" << h << ", " << k << ", " << l << "] @ TOF = " << peak->centre()
201 << " has unphysical parameters or unreasonable large FWHM"
202 << ".\n";
203 break;
204 }
205 }
206
207 return arevalid;
208}
209
210//----------------------------------------------------------------------------------------------
213void LeBailFunction::calculatePeakParameterValues() const {
214 for (size_t i = 0; i < m_numPeaks; ++i) {
216 peak->calculateParameters(false);
217 }
218
219 m_hasNewPeakValue = false;
220}
221
222//----------------------------------------------------------------------------------------------
228void LeBailFunction::setPeakCentreTolerance(double peakpostol, double tofmin, double tofmax) {
229 // m_usePeakPosTol = true;
230 m_minTOFPeakCentre = tofmin - peakpostol;
231 m_maxTOFPeakCentre = tofmax + peakpostol;
232}
233
234//----------------------------------------------------------------------------------------------
238void LeBailFunction::addPeaks(const std::vector<std::vector<int>> &peakhkls) {
239 // Prerequisit
240 if (!m_isInputValue)
241 throw runtime_error("Client must set up profile parameter vlaues by calling "
242 "setProfileParameterValues() first! ");
243
244 // Add peaks
245 for (size_t ipk = 0; ipk < peakhkls.size(); ++ipk) {
246 vector<int> hkl = peakhkls[ipk];
247
248 // Check input Miller Index
249 if (hkl.size() != 3) {
250 stringstream errss;
251 errss << "Error of " << ipk << "-th input Miller Index. It has " << peakhkls[ipk].size()
252 << " items, but not required 3 items.";
253 g_log.error(errss.str());
254 throw runtime_error(errss.str());
255 }
256
257 // Generate new peak
258 int h = hkl[0];
259 int k = hkl[1];
260 int l = hkl[2];
262 if (!newpeak) {
263 g_log.error("Unable to generate peak. ");
264 throw runtime_error("Unable to generate peak.");
265 }
266
267 double tofh = newpeak->centre();
268 if (tofh < m_minTOFPeakCentre || tofh > m_maxTOFPeakCentre) {
269 g_log.information() << "Peak " << h << ", " << k << ", " << l << " 's centre is at TOF = " << tofh
270 << ", which is out of user specified boundary (" << m_minTOFPeakCentre << ", "
271 << m_maxTOFPeakCentre << "). "
272 << ".\n";
273 } else {
274 double dsp = newpeak->getPeakParameter("d_h");
275
276 // Add new peak to all related data storage
277 m_vecPeaks.emplace_back(newpeak);
278 // FIXME - Refining lattice size is not considered here!
279 m_dspPeakVec.emplace_back(dsp, newpeak);
280 m_mapHKLPeak.emplace(hkl, newpeak);
281 }
282 }
283
284 m_numPeaks = m_vecPeaks.size();
285
286 g_log.information() << "Total " << m_numPeaks << " after trying to add " << peakhkls.size() << " peaks. \n";
287} // END of addPeaks()
288
289//----------------------------------------------------------------------------------------------
295IPowderDiffPeakFunction_sptr LeBailFunction::generatePeak(int h, int k, int l) {
296 IFunction_sptr f = FunctionFactory::Instance().createFunction(m_peakType);
297 IPowderDiffPeakFunction_sptr peak = std::dynamic_pointer_cast<IPowderDiffPeakFunction>(f);
298
299 peak->setMillerIndex(h, k, l);
300 for (const auto &parname : m_peakParameterNameVec) {
301 double parvalue = m_functionParameters[parname];
302 peak->setParameter(parname, parvalue);
303 }
304
305 return peak;
306}
307
308//----------------------------------------------------------------------------------------------
323bool LeBailFunction::calculatePeaksIntensities(std::span<double const> const vecX, std::span<double const> const vecY,
324 vector<double> &vec_summedpeaks) {
325 // Clear inputs
326 std::fill(vec_summedpeaks.begin(), vec_summedpeaks.end(), 0.0);
327
328 // Divide peaks into groups from peak's parameters
329 vector<vector<pair<double, IPowderDiffPeakFunction_sptr>>> peakgroupvec;
330 vector<IPowderDiffPeakFunction_sptr> outboundpeakvec;
331 double xmin = vecX.front();
332 double xmax = vecX.back();
333 groupPeaks(peakgroupvec, outboundpeakvec, xmin, xmax);
334
335 // Calculate each peak's intensity and set
336 bool allpeakheightsphysical = true;
337 for (size_t ig = 0; ig < peakgroupvec.size(); ++ig) {
338 g_log.debug() << "[Fx351] Calculate peaks heights for (peak) group " << ig
339 << " : number of peaks = " << peakgroupvec[ig].size() << "\n";
340
341 bool peakheightsphysical = calculateGroupPeakIntensities(peakgroupvec[ig], vecX, vecY, vec_summedpeaks);
342
343 if (!peakheightsphysical)
344 allpeakheightsphysical = false;
345 }
346
347 // Set zero to all peaks out of boundary
348 for (const auto &peak : outboundpeakvec) {
349 peak->setHeight(0.);
350 }
351
352 return allpeakheightsphysical;
353}
354
355//----------------------------------------------------------------------------------------------
366bool LeBailFunction::calculateGroupPeakIntensities(vector<pair<double, IPowderDiffPeakFunction_sptr>> peakgroup,
367 std::span<double const> const vecX,
368 std::span<double const> const vecY,
369 vector<double> &vec_summedpeaks) {
370 // Check input peaks group and sort peak by d-spacing
371 if (peakgroup.empty()) {
372 throw runtime_error("Programming error such that input peak group cannot be empty!");
373 } else {
374 g_log.debug() << "[Fx155] Peaks group size = " << peakgroup.size() << "\n";
375 }
376 if (peakgroup.size() > 1)
377 sort(peakgroup.begin(), peakgroup.end());
378
379 // Check input vector validity
380 if (vec_summedpeaks.size() != vecY.size()) {
381 stringstream errss;
382 errss << "Input vector 'allpeaksvalues' has wrong size = " << vec_summedpeaks.size()
383 << " != data workspace Y's size = " << vecY.size();
384 g_log.error(errss.str());
385 throw runtime_error(errss.str());
386 }
387
388 // Check boundary
389 IPowderDiffPeakFunction_sptr leftpeak = peakgroup[0].second;
390 double leftbound = leftpeak->centre() - PEAKRANGECONSTANT * leftpeak->fwhm();
391 if (leftbound < vecX.front()) {
392 stringstream msg;
393 int h, k, l;
394 leftpeak->getMillerIndex(h, k, l);
395 msg << "Peak group (containing " << peakgroup.size() << " peaks) has its left boundary (TOF = " << leftbound
396 << ") out side of input data workspace's left boundary (" << vecX.front()
397 << "). Accuracy of its peak intensity might be affected. "
398 << "Group's left boundary is determined by its leftmost peak (" << h << ", " << k << ", " << l
399 << ") at TOF = " << leftpeak->centre() << " with FWHM = " << leftpeak->fwhm() << ". ";
400
401 g_log.information(msg.str());
402
403 leftbound = vecX[0] + 0.1;
404 }
405 IPowderDiffPeakFunction_sptr rightpeak = peakgroup.back().second;
406 double rightbound = rightpeak->centre() + PEAKRANGECONSTANT * rightpeak->fwhm();
407 if (rightbound > vecX.back()) {
408 stringstream msg;
409 msg << "Peak group's right boundary " << rightbound << " is out side of "
410 << "input data workspace's right bound (" << vecX.back()
411 << ")! Accuracy of its peak intensity might be affected. ";
412
413 g_log.information(msg.str());
414
415 rightbound = vecX.back() - 0.1;
416 }
417
418 // Determine calculation range to input workspace: [ileft, iright)
419 auto cviter = lower_bound(vecX.begin(), vecX.end(), leftbound);
420 size_t ileft = static_cast<size_t>(cviter - vecX.begin());
421 if (ileft > 0)
422 --ileft;
423
424 cviter = lower_bound(vecX.begin(), vecX.end(), rightbound);
425 size_t iright = static_cast<size_t>(cviter - vecX.begin());
426 if (iright <= vecX.size() - 1)
427 ++iright;
428
429 size_t ndata = iright - ileft;
430 if (ileft >= iright) {
431 stringstream errss;
432 errss << "[Calcualte Peak Intensity] Group range is unphysical. iLeft = " << ileft << ", iRight = " << iright
433 << "; Number of peaks = " << peakgroup.size() << "; Left boundary = " << leftbound
434 << ", Right boundary = " << rightbound << "; Left peak FWHM = " << leftpeak->fwhm()
435 << ", Right peak FWHM = " << rightpeak->fwhm();
436 for (size_t ipk = 0; ipk < peakgroup.size(); ++ipk) {
437 IPowderDiffPeakFunction_sptr thispeak = peakgroup[ipk].second;
438 errss << "Peak " << ipk << ": d_h = " << peakgroup[ipk].first << ", TOF_h = " << thispeak->centre()
439 << ", FWHM = " << thispeak->fwhm() << "\n";
440 vector<string> peakparamnames = thispeak->getParameterNames();
441 for (auto &peakparamname : peakparamnames) {
442 errss << "\t" << peakparamname << " = " << thispeak->getParameter(peakparamname) << "\n";
443 }
444 }
445
446 g_log.error(errss.str());
447 throw runtime_error(errss.str());
448 }
449
450 // Generate a subset of vecX and vecY to calculate peak intensities
451 vector<double> datax(vecX.begin() + ileft, vecX.begin() + iright);
452 vector<double> datay(vecY.begin() + ileft, vecY.begin() + iright);
453 if (datax.size() != ndata) {
454 stringstream errmsg;
455 errmsg << "Impossible: Partial peak data size = " << datax.size() << " != ndata = " << ndata;
456 g_log.error(errmsg.str());
457 throw runtime_error(errmsg.str());
458 }
459 g_log.debug() << "[DBx356] Number of data points = " << ndata << " index from " << ileft << " to " << iright
460 << "; Size(datax, datay) = " << datax.size() << "\n";
461
462 // Prepare to integrate dataY to calculate peak intensity
463 vector<double> sumYs(ndata, 0.0);
464 size_t numPeaks(peakgroup.size());
465 vector<vector<double>> peakvalues(numPeaks);
466
467 // Integrage peak by peak
468 bool datavalueinvalid = false;
469 for (size_t ipk = 0; ipk < numPeaks; ++ipk) {
470 // calculate peak function value. Peak height should be set to a non-zero
471 // value
472 IPowderDiffPeakFunction_sptr peak = peakgroup[ipk].second;
473 peak->setHeight(1.0);
474 vector<double> localpeakvalue(ndata, 0.0);
475 peak->function(localpeakvalue, datax);
476
477 // check data
478 const auto numbadpts = std::count_if(localpeakvalue.cbegin(), localpeakvalue.cend(), [&](const auto &pt) {
479 return (pt != 0.) && (pt < NEG_DBL_MAX || pt > DBL_MAX);
480 });
481
482 // report the problem and/or integrate data
483 if (numbadpts == 0) {
484 // Data is fine. Integrate them all
485 for (size_t i = 0; i < ndata; ++i) {
486 // If value is physical
487 sumYs[i] += localpeakvalue[i];
488 }
489 } else {
490 // Report the problem
491 int h, k, l;
492 peak->getMillerIndex(h, k, l);
493 stringstream warnss;
494 warnss << "Peak (" << h << ", " << k << ", " << l << ") @ TOF = " << peak->centre() << " has " << numbadpts
495 << " data points, "
496 << "whose values exceed limit (i.e., not physical). ";
497 g_log.debug(warnss.str());
498 datavalueinvalid = true;
499 }
500 peakvalues[ipk].assign(localpeakvalue.begin(), localpeakvalue.end());
501 } // For All peaks
502
503 // Calculate intensity of all peaks
504 bool peakheightsphysical = !datavalueinvalid;
505 if (peakheightsphysical) {
506 for (size_t ipk = 0; ipk < peakgroup.size(); ++ipk) {
507 IPowderDiffPeakFunction_sptr peak = peakgroup[ipk].second;
508 double intensity = 0.0;
509
510 for (size_t i = 0; i < ndata; ++i) {
511 double temp;
512 if (sumYs[i] > 1.0E-5) {
513 // Reasonable non-zero value
514 double peaktogroupratio = peakvalues[ipk][i] / sumYs[i];
515 temp = datay[i] * peaktogroupratio;
516 } else {
517 // SumY too smaller
518 temp = 0.0;
519 }
520 double deltax;
521 if (i == 0)
522 deltax = datax[1] - datax[0];
523 else
524 deltax = datax[i] - datax[i - 1];
525
526 intensity += temp * deltax;
527 } // for data points
528
529 if (intensity != intensity) {
530 // Unphysical intensity: NaN
531 intensity = 0.0;
532 peakheightsphysical = false;
533
534 int h, k, l;
535 peak->getMillerIndex(h, k, l);
536 g_log.warning() << "Peak (" << h << ", " << k << ", " << l << ") has unphysical intensity = NaN!\n";
537
538 } else if (intensity <= -DBL_MAX || intensity >= DBL_MAX) {
539 // Unphysical intensity: NaN
540 intensity = 0.0;
541 peakheightsphysical = false;
542
543 int h, k, l;
544 peak->getMillerIndex(h, k, l);
545 g_log.warning() << "Peak (" << h << ", " << k << ", " << l << ") has unphysical intensity = Infty!\n";
546 } else if (intensity < 0.0) {
547 // No negative intensity
548 g_log.debug() << "[Fx134] Set peak @ " << peak->centre() << "'s intensity to 0.0 instead of " << intensity
549 << ".\n";
550 intensity = 0.0;
551 }
552 g_log.debug() << "[Fx407] Peak @ " << peak->centre() << ": Set Intensity = " << intensity << "\n";
553 peak->setHeight(intensity);
554
555 // Add peak's value to peaksvalues
556 for (size_t i = ileft; i < iright; ++i) {
557 vec_summedpeaks[i] += (intensity * peakvalues[ipk][i - ileft]);
558 }
559
560 } // ENDFOR each peak
561 }
562
563 return peakheightsphysical;
564}
565
566//----------------------------------------------------------------------------------------------
575void LeBailFunction::setPeakParameters(const IPowderDiffPeakFunction_sptr &peak, const map<string, double> &parammap,
576 double peakheight, bool setpeakheight) {
577 UNUSED_ARG(peak);
578 UNUSED_ARG(parammap);
579 UNUSED_ARG(peakheight);
580 UNUSED_ARG(setpeakheight);
581 throw runtime_error("Requiring update flag: peak value changed and etc.");
582 /*
583 // FIXME - The best solution for speeding is to have a set of peak
584 parameter listed in the order
585 // of peak function's parameters' indexed. Then no need to do
586 search anymore.
587
588 // 1. Prepare, sort parameters by name
589 std::map<std::string, double>::iterator pit;
590 vector<string> peakparamnames = peak->getParameterNames();
591
592 // 2. Apply parameters values to peak function
593 for (pit = parammap.begin(); pit != parammap.end(); ++pit)
594 {
595 // a) Check whether the parameter is a peak parameter
596 std::string parname = pit->first;
597 std::vector<std::string>::iterator ifind =
598 std::find(peakparamnames.begin(), peakparamnames.end(), parname);
599
600 // b) Set parameter value
601 if (ifind == peakparamnames.end())
602 {
603 // If not a peak profile parameter, skip
604 g_log.debug() << "Parameter '" << parname << "' in input parameter
605 table workspace "
606 << "is not for peak function " << peak->name() << ".\n";
607 }
608 else
609 {
610 // Set value
611 double value = pit->second;
612 peak->setParameter(parname, value);
613 g_log.debug() << "LeBailFit Set " << parname << "= " << value << "\n";
614 }
615 } // ENDFOR: parameter iterator
616
617 // 3. Peak height
618 if (setpeakheight)
619 peak->setHeight(peakheight);
620
621 return;*/
622}
623
624//----------------------------------------------------------------------------------------------
635void LeBailFunction::setProfileParameterValues(map<std::string, double> parammap) {
636 const double MINDIFF = 1.0E-10;
637
638 map<std::string, double>::iterator inpiter, curiter;
639
640 size_t numpars = m_peakParameterNameVec.size();
641 for (size_t i = 0; i < numpars; ++i) {
642 string &parname = m_peakParameterNameVec[i];
643
644 // Find iterator of this parameter in input parammap
645 inpiter = parammap.find(parname);
646 if (inpiter != parammap.end()) {
647 // Find iterator to parameter value in class' parameter map (parameter is
648 // found in input map)
649 curiter = m_functionParameters.find(parname);
650 if (curiter == m_functionParameters.end()) {
651 stringstream errmsg;
652 errmsg << "Parameter " << parname << " is in parameter name list, but not in profile "
653 << "parameter map. It violates the programming logic.";
654 g_log.error(errmsg.str());
655 throw runtime_error(errmsg.str());
656 }
657
658 // Set value if difference is large
659 double curvalue = curiter->second;
660 double newvalue = inpiter->second;
661 bool localnewvalue = false;
662 if (fabs(curvalue - newvalue) > MINDIFF) {
663 curiter->second = newvalue;
664 m_hasNewPeakValue = true;
665 localnewvalue = true;
666 }
667
668 // Set new value to each peak
669 if (!localnewvalue)
670 continue;
671
672 // Set new parameter to each peak
673 for (size_t ipk = 0; ipk < m_numPeaks; ++ipk) {
675 peak->setParameter(i, newvalue);
676 }
677 } // If parameter name is a profile parameter
678 else {
679 g_log.debug() << "Parameter " << parname << " is not a profile parameter. Length of string = " << parname.size()
680 << "\n";
681 }
682 } // ENDFOR [All profile parameter]
683
684 // Set the flag to indicate that client has input parameters
686 m_isInputValue = true;
687}
688
689//----------------------------------------------------------------------------------------------
697void LeBailFunction::groupPeaks(vector<vector<pair<double, IPowderDiffPeakFunction_sptr>>> &peakgroupvec,
698 vector<IPowderDiffPeakFunction_sptr> &outboundpeakvec, double xmin, double xmax) {
699 // Sort peaks
700 if (m_numPeaks > 1) {
701 sort(m_dspPeakVec.begin(), m_dspPeakVec.end());
702 } else if (m_numPeaks == 0) {
703 std::stringstream errmsg;
704 errmsg << "Group peaks: No peak is found in the peak vector. ";
705 g_log.error() << errmsg.str() << "\n";
706 throw std::runtime_error(errmsg.str());
707 }
708
709 // Set up starting value
710 peakgroupvec.clear();
711 outboundpeakvec.clear();
712 vector<pair<double, IPowderDiffPeakFunction_sptr>> peakgroup; // one group of peaks
713 size_t ipk = 0;
714
715 // Group peaks from low-d to high-d
716 bool outbound = true;
717 while (outbound && ipk < m_numPeaks) {
718 // Group peaks out of lower boundary to a separate vector of peaks
720 if (peak->centre() <= xmin) {
721 // Add peak
722 outboundpeakvec.emplace_back(peak);
723 ipk += 1;
724 } else {
725 // Get out of while loop if peak is in bound
726 outbound = false;
727 }
728 }
729
730 bool inbound = true;
731 while (inbound && ipk < m_numPeaks) {
732 // Group peaks in the boundary
733 IPowderDiffPeakFunction_sptr thispeak = m_dspPeakVec[ipk].second;
734
735 if (thispeak->centre() < xmax) {
736 // Peak is in the boundary still
737
738 // add peak to CURRENT peak group
739 peakgroup.emplace_back(m_dspPeakVec[ipk]);
740
741 if (ipk < m_numPeaks - 1) {
742 // Any peak but not the last (rightmost) peak
743
744 // test whether next peak will be in a different group
745 IPowderDiffPeakFunction_sptr rightpeak = m_dspPeakVec[ipk + 1].second;
746
747 double thispeak_rightbound = thispeak->centre() + PEAKRANGECONSTANT * thispeak->fwhm();
748 double rightpeak_leftbound = rightpeak->centre() - PEAKRANGECONSTANT * rightpeak->fwhm();
749
750 if (thispeak_rightbound < rightpeak_leftbound) {
751 // this peak and its right peak are well separated.
752 // finish this group by swapping values
753 peakgroupvec.emplace_back(std::move(peakgroup));
754 peakgroup = {};
755 } else {
756 // this peak and its right peak are close enough to be in same group.
757 // do nothing
758 ;
759 }
760 } else {
761 // Rightmost peak. Finish the current peak
762 peakgroupvec.emplace_back(peakgroup);
763 }
764
765 ++ipk;
766 } // still in bound
767 else {
768 // Peak is get out of boundary
769 inbound = false;
770 g_log.information() << "[Fx301] Group peak: peak @ " << thispeak->centre() << " causes grouping "
771 << "peak over at maximum TOF = " << xmax << ".\n";
772
773 if (!peakgroup.empty()) {
774 peakgroupvec.emplace_back(peakgroup);
775 }
776 } // FIRST out of boundary
777 } // ENDWHILE
778
779 while (ipk < m_numPeaks) {
780 // Group peaks out of uppper boundary to a separate vector of peaks
781 outboundpeakvec.emplace_back(m_dspPeakVec[ipk].second);
782 ipk += 1;
783 }
784
785 g_log.debug() << "[Calculate Peak Intensity]: Number of Peak Groups = " << peakgroupvec.size() << "\n";
786}
787
788//----------------------------------------------------------------------------------------------
799void LeBailFunction::addBackgroundFunction(const string &backgroundtype, const unsigned int &order,
800 const std::vector<std::string> &vecparnames,
801 const std::vector<double> &vecparvalues, double startx, double endx) {
802 // Check
803 if (backgroundtype != POLYNOMIAL_BACKGROUND && backgroundtype != CHEBYSHEV_BACKGROUND &&
804 backgroundtype != FULLPROF_POLYNOMIAL_BACKGROUND) {
805 stringstream warnss;
806 warnss << "Cliet specified background type " << backgroundtype << " may not be supported properly.";
807 g_log.warning(warnss.str());
808 }
809 if (vecparnames.size() != vecparvalues.size())
810 throw runtime_error("Input parameter names and parameter values are not matched. ");
811
812 g_log.information() << "Add background: type = " << backgroundtype << ", order = " << order
813 << ", number of parameters/attributes = " << vecparnames.size() << "\n";
814
815 // Create background function from factory
816 auto background = FunctionFactory::Instance().createFunction(backgroundtype);
817 m_background = std::dynamic_pointer_cast<Functions::BackgroundFunction>(background);
818
819 // Set order and initialize
820 m_background->setAttributeValue("n", static_cast<int>(order));
821 m_background->initialize();
822
823 // Set parameters & attribute
824 size_t numpars = vecparnames.size();
825 for (size_t i = 0; i < numpars; ++i) {
826 const string &parname = vecparnames[i];
827 if (parname != "Bkpos")
828 m_background->setParameter(parname, vecparvalues[i]);
829 else if (backgroundtype == FULLPROF_POLYNOMIAL_BACKGROUND)
830 m_background->setAttributeValue("Bkpos", vecparvalues[i]);
831 else
832 throw runtime_error("Bkpos should not be in the parameter list. ");
833 }
834
835 if (backgroundtype == CHEBYSHEV_BACKGROUND) {
836 if (startx > 0.)
837 m_background->setAttributeValue("StartX", startx);
838 if (endx > 0.)
839 m_background->setAttributeValue("EndX", endx);
840 }
841}
842
843//----------------------------------------------------------------------------------------------
849void LeBailFunction::setFitProfileParameter(const string &paramname, double minvalue, double maxvalue) {
850 // Make ties in composition function
851 for (size_t ipk = 1; ipk < m_numPeaks; ++ipk) {
852 stringstream ss1, ss2;
853 ss1 << "f" << (ipk - 1) << "." << paramname;
854 ss2 << "f" << ipk << "." << paramname;
855 string tiepart1 = ss1.str();
856 string tiepart2 = ss2.str();
857 m_compsiteFunction->tie(tiepart1, tiepart2);
858 g_log.debug() << "LeBailFunction::Fit(Tie) / " << tiepart1 << " / " << tiepart2 << " /\n";
859 }
860
861 // Set contrains of the parameter on any of the tied parameter.
862 std::stringstream parss;
863 parss << "f0." << paramname;
864 string parnamef0 = parss.str();
865 auto bc = std::make_unique<Constraints::BoundaryConstraint>(m_compsiteFunction.get(), parnamef0, minvalue, maxvalue);
866 m_compsiteFunction->addConstraint(std::move(bc));
867}
868
869//----------------------------------------------------------------------------------------------
874void LeBailFunction::fixPeakParameter(const string &paramname, double paramvalue) {
875 for (size_t ipk = 0; ipk < m_numPeaks; ++ipk) {
876 stringstream ss1, ss2;
877 ss1 << "f" << ipk << "." << paramname;
878 ss2 << paramvalue;
879 string tiepart1 = ss1.str();
880 string tievalue = ss2.str();
881 m_compsiteFunction->tie(tiepart1, tievalue);
882
883 g_log.debug() << "Set up tie | " << tiepart1 << " <---> " << tievalue << " | \n";
884
885 // FIXME & TODO: Make a map between peak parameter name and index. And use
886 // fix() to replace tie
887 /*-- Code prepared to replace the existing block
888 ThermalNeutronBk2BkExpConvPVoigt_sptr thispeak = m_dspPeaks[ipk].second;
889 size_t iparam = findIndex(thispeak, funcparam.name);
890 thispeak->fix(iparam);
891 --*/
892
893 } // For each peak
894}
895
896//----------------------------------------------------------------------------------------------
899void LeBailFunction::fixBackgroundParameters() {
900 size_t numbkgdparams = m_background->nParams();
901
902 for (size_t iparam = 0; iparam < numbkgdparams; ++iparam)
903 m_background->fix(iparam);
904}
905
906//----------------------------------------------------------------------------------------------
909void LeBailFunction::setFixPeakHeights() {
910 for (size_t ipk = 0; ipk < m_numPeaks; ++ipk) {
911 // a. Get peak height
912 IPowderDiffPeakFunction_sptr thispeak = m_dspPeakVec[ipk].second;
913 thispeak->fix(0);
914 } // For each peak
915}
916
917//----------------------------------------------------------------------------------------------
921void LeBailFunction::setPeakHeights(const std::vector<double> &inheights) {
922 UNUSED_ARG(inheights);
923 throw runtime_error("It is not implemented properly.");
924 /*
925 if (inheights.size() != heights.size())
926 {
927 g_log.error() << "Input number of peaks (height) is not same as peaks. "
928 << '\n';
929 throw std::logic_error("Input number of peaks (height) is not same as
930 peaks. ");
931 }
932
933 for (size_t ih = 0; ih < inheights.size(); ++ih)
934 heights[ih] = inheights[ih];
935
936 return;*/
937}
938
939//----------------------------------------------------------------------------------------------
942IPowderDiffPeakFunction_sptr LeBailFunction::getPeak(size_t peakindex) {
943 if (peakindex >= m_numPeaks) {
944 stringstream errmsg;
945 errmsg << "Try to access peak " << peakindex << " out of range [0, " << m_numPeaks << ").";
946 g_log.error(errmsg.str());
947 throw runtime_error(errmsg.str());
948 }
949
950 IPowderDiffPeakFunction_sptr rpeak = m_vecPeaks[peakindex];
951
952 return rpeak;
953}
954
955//----------------------------------------------------------------------------------------------
958double LeBailFunction::getPeakParameter(std::vector<int> hkl, const std::string &parname) const {
959 // Search peak in map
960 map<vector<int>, IPowderDiffPeakFunction_sptr>::const_iterator fiter;
961 fiter = m_mapHKLPeak.find(hkl);
962 if (fiter == m_mapHKLPeak.end()) {
963 stringstream errss;
964 errss << "Peak with Miller index (" << hkl[0] << ", " << hkl[1] << "," << hkl[2]
965 << ") does not exist in Le Bail function.";
966 g_log.error(errss.str());
967 throw runtime_error(errss.str());
968 }
969
970 IPowderDiffPeakFunction_sptr peak = fiter->second;
971
972 double parvalue = getPeakParameterValue(peak, parname);
973
974 return parvalue;
975}
976
977//----------------------------------------------------------------------------------------------
980double LeBailFunction::getPeakParameter(size_t index, const std::string &parname) const {
981 if (index >= m_numPeaks) {
982 stringstream errss;
983 errss << "getPeakParameter() tries to reach a peak with index " << index << ", which is out of range " << m_numPeaks
984 << "/" << m_vecPeaks.size() << ".";
985 g_log.error(errss.str());
986 throw std::runtime_error(errss.str());
987 }
988
990 double value = getPeakParameterValue(peak, parname);
991
992 return value;
993}
994
995//----------------------------------------------------------------------------------------------
1000double LeBailFunction::getPeakParameterValue(const API::IPowderDiffPeakFunction_sptr &peak,
1001 const std::string &parname) const {
1002 // Locate the category of the parameter name
1003 auto vsiter = lower_bound(m_orderedProfileParameterNames.cbegin(), m_orderedProfileParameterNames.cend(), parname);
1004
1005 bool found = true;
1006 if (vsiter == m_orderedProfileParameterNames.end()) {
1007 // End of vector
1008 found = false;
1009 } else {
1010 // Middle of vector. But no match
1011 string matchparname = *vsiter;
1012 if (parname != matchparname)
1013 found = false;
1014 }
1015
1016 // Get parameter
1017 double parvalue;
1018 if (found) {
1019 // It is a native peak parameter
1020 parvalue = peak->getParameter(parname);
1021 } else {
1022 // It is a calculated peak parameter
1023 parvalue = peak->getPeakParameter(parname);
1024 }
1025
1026 return parvalue;
1027}
1028
1029//----------------------------------------------------------------------------------------------
1032double LeBailFunction::getPeakMaximumValue(std::vector<int> hkl, const std::vector<double> &xvalues, size_t &ix) {
1033 // Search peak in map
1034 map<vector<int>, IPowderDiffPeakFunction_sptr>::const_iterator fiter;
1035 fiter = m_mapHKLPeak.find(hkl);
1036 if (fiter == m_mapHKLPeak.end()) {
1037 stringstream errss;
1038 errss << "Peak with Miller index (" << hkl[0] << ", " << hkl[1] << "," << hkl[2]
1039 << ") does not exist in Le Bail function.";
1040 g_log.error(errss.str());
1041 throw runtime_error(errss.str());
1042 }
1043
1044 IPowderDiffPeakFunction_sptr peak = fiter->second;
1045
1046 double maxvalue = peak->getMaximumValue(xvalues, ix);
1047
1048 return maxvalue;
1049}
1050
1051} // namespace Mantid::CurveFitting::Algorithms
double intensity
double background
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
#define fabs(x)
Definition Matrix.cpp:22
const double NEG_DBL_MAX
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition System.h:44
A composite function is a function containing other functions.
Implements FunctionDomain1D with its own storage in form of a std::vector.
A class to store values calculated by a function.
std::vector< std::string > m_peakParameterNameVec
Name of peak parameter names (be same as the order in IPowderDiffPeakFunction)
void calculatePeakParameterValues() const
Calculate all peaks' parameter value.
void groupPeaks(std::vector< std::vector< std::pair< double, API::IPowderDiffPeakFunction_sptr > > > &peakgroupvec, std::vector< API::IPowderDiffPeakFunction_sptr > &outboundpeakvec, double xmin, double xmax)
Group close peaks together.
std::vector< std::pair< double, API::IPowderDiffPeakFunction_sptr > > m_dspPeakVec
Vector of pair <peak position in d-space, Peak> sortable.
bool calculateGroupPeakIntensities(std::vector< std::pair< double, API::IPowderDiffPeakFunction_sptr > > peakgroup, std::span< double const > vecX, std::span< double const > vecY, std::vector< double > &vec_summedpeaks)
Calculate the peaks intensities in same group.
Functions::BackgroundFunction_sptr m_background
Background function.
API::IPowderDiffPeakFunction_sptr generatePeak(int h, int k, int l)
Generate a peak with parameter set by.
std::vector< API::IPowderDiffPeakFunction_sptr > m_vecPeaks
Vector of all peaks.
double getPeakParameterValue(const API::IPowderDiffPeakFunction_sptr &peak, const std::string &parname) const
Retrieve peak's parameter. may be native or calculated.
API::CompositeFunction_sptr m_compsiteFunction
Composite functions for all peaks and background.
std::vector< std::string > m_orderedProfileParameterNames
Ordered profile parameter names for search.
std::map< std::vector< int >, API::IPowderDiffPeakFunction_sptr > m_mapHKLPeak
Vector of all peak's Miller indexes.
std::map< std::string, double > m_functionParameters
Parameters.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
std::shared_ptr< IPowderDiffPeakFunction > IPowderDiffPeakFunction_sptr
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
Definition IFunction.h:748
std::shared_ptr< CompositeFunction > CompositeFunction_sptr
shared pointer to the composite function base class
Kernel::Logger g_log("DetermineSpinStateOrder")
static constexpr double h
Planck constant in J*s.
STL namespace.