Mantid
Loading...
Searching...
No Matches
LeBailFit.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 +
10#include "MantidAPI/TableRow.h"
11#include "MantidAPI/TextAxis.h"
15#include "MantidHistogramData/HistogramX.h"
16#include "MantidHistogramData/HistogramY.h"
20
21#include <boost/algorithm/string.hpp>
22
23#include <cctype>
24#include <fstream>
25#include <iomanip>
26
27const int OBSDATAINDEX(0);
28const int CALDATAINDEX(1);
29const int DATADIFFINDEX(2);
30const int CALPUREPEAKINDEX(3);
31// Output workspace background at ws index 4
32const int CALBKGDINDEX(4);
33// Input background
34const int INPUTBKGDINDEX(6);
35// Output workspace: pure peak (data with background removed)
36const int INPUTPUREPEAKINDEX(7);
37
38const double NOBOUNDARYLIMIT(1.0E10);
39const double EPSILON(1.0E-10);
40
41using namespace Mantid;
42using namespace Mantid::CurveFitting;
43using namespace Mantid::DataObjects;
44using namespace Mantid::API;
45using namespace Mantid::Kernel;
46using Mantid::HistogramData::HistogramX;
47using Mantid::HistogramData::HistogramY;
48
49using namespace std;
50
52
53const Rfactor badR(DBL_MAX, DBL_MAX);
54
56
57//----------------------------------------------------------------------------------------------
61 : m_lebailFunction(), m_dataWS(), m_outputWS(), parameterWS(), reflectionWS(), m_wsIndex(0), m_startX(DBL_MAX),
62 m_endX(DBL_MIN), m_inputPeakInfoVec(), m_backgroundFunction(), m_funcParameters(), m_origFuncParameters(),
63 m_peakType(), m_backgroundType(), m_backgroundParameters(), m_backgroundParameterNames(), m_bkgdorder(0),
64 mPeakRadius(0), m_lebailFitChi2(0.), m_lebailCalChi2(0.), mMinimizer(), m_dampingFactor(0.),
65 m_inputParameterPhysical(false), m_fitMode(), m_indicatePeakHeight(0.), m_MCGroups(), m_numMCGroups(0),
66 m_bestRwp(0.), m_bestRp(0.), m_bestParameters(), m_bestBackgroundData(), m_bestMCStep(0), m_numMinimizeSteps(0),
67 m_Temperature(DBL_MIN), m_useAnnealing(false), m_walkStyle(RANDOMWALK), m_minimumPeakHeight(DBL_MAX),
68 m_tolerateInputDupHKL2Peaks(false), m_bkgdParameterNames(), m_numberBkgdParameters(0), m_bkgdParameterBuffer(),
69 m_bestBkgdParams(), m_roundBkgd(0), m_bkgdParameterStepVec(), m_peakCentreTol(0.) {}
70
71//----------------------------------------------------------------------------------------------
75 // -------------- Input and output Workspaces -----------------
76
77 // Input Data Workspace
78 this->declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input),
79 "Input workspace containing the data to fit by LeBail algorithm.");
80
81 // Output Result Data/Model Workspace
82 this->declareProperty(std::make_unique<WorkspaceProperty<Workspace2D>>("OutputWorkspace", "", Direction::Output),
83 "Output workspace containing calculated pattern or "
84 "calculated background. ");
85
86 // Instrument profile Parameters
87 this->declareProperty(
88 std::make_unique<WorkspaceProperty<TableWorkspace>>("InputParameterWorkspace", "", Direction::Input),
89 "Input table workspace containing the parameters "
90 "required by LeBail fit. ");
91
92 // Output instrument profile parameters
93 auto tablewsprop1 = std::make_unique<WorkspaceProperty<TableWorkspace>>(
94 "OutputParameterWorkspace", "", Direction::Output, API::PropertyMode::Optional);
95 this->declareProperty(std::move(tablewsprop1), "Input table workspace containing the "
96 "parameters required by LeBail fit. ");
97
98 // Single peak: Reflection (HKL) Workspace, PeaksWorkspace
99 this->declareProperty(std::make_unique<WorkspaceProperty<TableWorkspace>>("InputHKLWorkspace", "", Direction::Input),
100 "Input table workspace containing the list of reflections (HKL). ");
101
102 // Bragg peaks profile parameter output table workspace
103 auto tablewsprop2 = std::make_unique<WorkspaceProperty<TableWorkspace>>("OutputPeaksWorkspace", "", Direction::Output,
105 this->declareProperty(std::move(tablewsprop2), "Optional output table workspace "
106 "containing all peaks' peak "
107 "parameters. ");
108
109 // WorkspaceIndex
110 this->declareProperty("WorkspaceIndex", 0, "Workspace index of the spectrum to fit by LeBail.");
111
112 // Interested region
113 this->declareProperty(std::make_unique<Kernel::ArrayProperty<double>>("FitRegion"),
114 "Region of data (TOF) for LeBail fit. Default is whole range. ");
115
116 // Functionality: Fit/Calculation/Background
117 std::vector<std::string> functions{"LeBailFit", "Calculation", "MonteCarlo", "RefineBackground"};
118 auto validator = std::make_shared<Kernel::StringListValidator>(functions);
119 this->declareProperty("Function", "LeBailFit", validator, "Functionality");
120
121 // Peak type
122 vector<string> peaktypes{"ThermalNeutronBk2BkExpConvPVoigt", "NeutronBk2BkExpConvPVoigt"};
123 auto peaktypevalidator = std::make_shared<StringListValidator>(peaktypes);
124 declareProperty("PeakType", "ThermalNeutronBk2BkExpConvPVoigt", peaktypevalidator, "Peak profile type.");
125
126 /*------------------------ Background Related Properties
127 * ---------------------------------*/
128 // About background: Background type, input (table workspace or array)
129 std::vector<std::string> bkgdtype{"Polynomial", "Chebyshev", "FullprofPolynomial"};
130 auto bkgdvalidator = std::make_shared<Kernel::StringListValidator>(bkgdtype);
131 declareProperty("BackgroundType", "Polynomial", bkgdvalidator, "Background type");
132
133 // Input background parameters (array)
134 this->declareProperty(std::make_unique<Kernel::ArrayProperty<double>>("BackgroundParameters"),
135 "Optional: enter a comma-separated list of background order parameters "
136 "from order 0. ");
137
138 // Input background parameters (tableworkspace)
139 auto tablewsprop3 = std::make_unique<WorkspaceProperty<TableWorkspace>>(
140 "BackgroundParametersWorkspace", "", Direction::InOut, API::PropertyMode::Optional);
141 this->declareProperty(std::move(tablewsprop3), "Optional table workspace containing the fit result for background.");
142
143 // Peak Radius
144 this->declareProperty("PeakRadius", 5, "Range (multiplier relative to FWHM) for a full peak. ");
145
146 /*------------------------ Properties for Calculation Mode
147 * --------------------------------*/
148 // Output option to plot each individual peak
149 declareProperty("PlotIndividualPeaks", false, "Option to output each individual peak in mode Calculation.");
150 setPropertySettings("PlotIndividualPeaks",
151 std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "Calculation"));
152
153 // Make each reflection visible
154 declareProperty("IndicationPeakHeight", 0.0,
155 "Heigh of peaks (reflections) if its calculated height is "
156 "smaller than user-defined minimum.");
157 setPropertySettings("IndicationPeakHeight",
158 std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "Calculation"));
159
160 // UseInputPeakHeights
161 declareProperty("UseInputPeakHeights", true,
162 "For 'Calculation' mode only, use peak heights specified in "
163 "ReflectionWorkspace. "
164 "Otherwise, calcualte peaks' heights. ");
165 setPropertySettings("UseInputPeakHeights",
166 std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "Calculation"));
167
168 /*--------------------------- Properties for Fitting Mode
169 * ---------------------------------*/
170 // Minimizer
171 std::vector<std::string> minimizerOptions = API::FuncMinimizerFactory::Instance().getKeys(); // :Instance().getKeys();
172 declareProperty("Minimizer", "Levenberg-MarquardtMD",
174 "The minimizer method applied to do the fit, default is "
175 "Levenberg-Marquardt",
177 setPropertySettings("Minimizer", std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "LeBailFit"));
178
179 declareProperty("Damping", 1.0, "Damping factor if minimizer is 'Damped Gauss-Newton'");
180 setPropertySettings("Damping", std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "LeBailFit"));
181 setPropertySettings("Damping", std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "MonteCarlo"));
182
183 declareProperty("NumberMinimizeSteps", 100, "Number of Monte Carlo random walk steps.");
184 setPropertySettings("NumberMinimizeSteps",
185 std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "LeBailFit"));
186 setPropertySettings("NumberMinimizeSteps",
187 std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "MonteCarlo"));
188 setPropertySettings("NumberMinimizeSteps",
189 std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "RefineBackground"));
190
191 //----------------- Parameters for Monte Carlo Simulated Annealing
192 //--------------------------
193 auto mcwsprop = std::make_unique<WorkspaceProperty<TableWorkspace>>("MCSetupWorkspace", "", Direction::Input,
195 declareProperty(std::move(mcwsprop), "Name of table workspace containing parameters' "
196 "setup for Monte Carlo simualted annearling. ");
197 setPropertySettings("MCSetupWorkspace", std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "MonteCarlo"));
198
199 declareProperty("RandomSeed", 1, "Random number seed.");
200 setPropertySettings("RandomSeed", std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "MonteCarlo"));
201
202 declareProperty("AnnealingTemperature", 1.0,
203 "Temperature used Monte Carlo. "
204 "Negative temperature is for simulated annealing. ");
205 setPropertySettings("AnnealingTemperature",
206 std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "MonteCarlo"));
207
208 declareProperty("UseAnnealing", true, "Allow annealing temperature adjusted automatically.");
209 setPropertySettings("UseAnnealing", std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "MonteCarlo"));
210
211 declareProperty("DrunkenWalk", false,
212 "Flag to use drunken walk algorithm. "
213 "Otherwise, random walk algorithm is used. ");
214 setPropertySettings("DrunkenWalk", std::make_unique<VisibleWhenProperty>("Function", IS_EQUAL_TO, "MonteCarlo"));
215
216 declareProperty("MinimumPeakHeight", 0.01,
217 "Minimum height of a peak to be counted "
218 "during smoothing background by exponential smooth algorithm. ");
219
220 // Flag to allow input hkl file containing degenerated peaks
221 declareProperty("AllowDegeneratedPeaks", false,
222 "Flag to allow degenerated peaks in input .hkl file. "
223 "Otherwise, an exception will be thrown if this situation occurs.");
224
225 // Tolerance of imported peak's position comparing to data range
226 declareProperty("ToleranceToImportPeak", EMPTY_DBL(),
227 "Tolerance in TOF to import peak from Bragg "
228 "peaks list. If it specified, all peaks within Xmin-Tol and "
229 "Xmax+Tol will be imported. "
230 "It is used in the case that the geometry parameters are "
231 "close to true values. ");
232}
233
234//----------------------------------------------------------------------------------------------
238 // Process input properties
240
241 // Import parameters from table workspace
244
245 // Background function and calculation on it
247
248 // Create Le Bail function
250
251 // Create output workspace/workspace
253
254 // 5. Adjust function mode according to input values
255 if (m_lebailFunction->isParameterValid()) {
256 // All peaks within range are physical and good to refine
258 } else {
259 // Some peaks within range have unphysical parameters. Just calcualtion for
260 // reference
262 g_log.warning() << "Input instrument parameters values cause some peaks to have "
263 "unphysical profile parameters.\n";
264 if (m_fitMode == FIT || m_fitMode == MONTECARLO) {
265 g_log.warning() << "Function mode FIT is disabled. Convert to Calculation mode.\n";
267 }
268 }
269
270 // 7. Do calculation or fitting
271 m_lebailFitChi2 = -1; // Initialize
272 m_lebailCalChi2 = -1;
273
274 switch (m_fitMode) {
275 case CALCULATION:
276 // Calculation
277 g_log.notice() << "Function: Pattern Calculation.\n";
279 break;
280
281 case FIT:
282 // LeBail Fit
283 g_log.notice() << "Function: Do LeBail Fit ==> Monte Carlo.\n";
284 // fall through
285 case MONTECARLO:
286 // Monte carlo Le Bail refinement
287 g_log.notice("Function: Do LeBail Fit By Monte Carlo Random Walk.");
289
290 break;
291
293 // Calculating background
294 // FIXME : Determine later whether this functionality is kept or removed!
295 g_log.notice() << "Function: Refine Background (Precisely).\n";
297 break;
298
299 default:
300 // Impossible
301 std::stringstream errmsg;
302 errmsg << "FunctionMode = " << m_fitMode << " is not supported in exec().";
303 g_log.error() << errmsg.str() << "\n";
304 throw std::runtime_error(errmsg.str());
305
306 break;
307 }
308
309 // 7. Output peak (table) and parameter workspace
312
313 setProperty("OutputWorkspace", m_outputWS);
314
315 // 8. Final statistic
316 Rfactor finalR = getRFactor(m_outputWS->y(0).rawData(), m_outputWS->y(1).rawData(), m_outputWS->e(0).rawData());
317 g_log.notice() << "\nFinal R factor: Rwp = " << finalR.Rwp << ", Rp = " << finalR.Rp
318 << ", Data points = " << m_outputWS->y(1).size() << ", Range = " << m_outputWS->x(0)[0] << ", "
319 << m_outputWS->x(0).back() << "\n";
320}
321
322//----------------------------------------------------------------------------------------------
327 // FIXME - Need to think of FullprofPolynomial
328 // Type
329 m_backgroundType = getPropertyValue("BackgroundType");
330
331 // Parameters
332 m_backgroundParameters = getProperty("BackgroundParameters");
333 TableWorkspace_sptr bkgdparamws = getProperty("BackgroundParametersWorkspace");
334
335 // Determine where the background parameters are from
336 if (!bkgdparamws) {
337 // Set up parameter name
339 size_t i0 = 0;
340 if (m_backgroundType == "FullprofPolynomial") {
341 // TODO - Add this special case to Wiki
342 m_backgroundParameterNames.emplace_back("Bkpos");
344 g_log.warning("Bkpos is out side of data range. It MIGHT NOT BE RIGHT. ");
345 i0 = 1;
346 }
347
348 size_t numparams = m_backgroundParameters.size();
349 for (size_t i = i0; i < numparams; ++i) {
350 stringstream parss;
351 parss << "A" << (i - i0);
352 m_backgroundParameterNames.emplace_back(parss.str());
353 }
354
355 g_log.information() << "[Input] Use background specified with vector with "
356 "input vector sized "
357 << numparams << ".\n";
358 } else {
359 g_log.information() << "[Input] Use background specified by table workspace.\n";
361 }
362
363 // Set up background order
364 m_bkgdorder = static_cast<unsigned int>(m_backgroundParameterNames.size());
365 if (m_backgroundType == "FullprofPolynomial") {
366 // Consider 1 extra Bkpos
367 if (m_bkgdorder == 0)
368 throw runtime_error("FullprofPolynomial: Bkpos must be given! ");
369 else if (m_bkgdorder <= 7)
370 m_bkgdorder = 6;
371 else if (m_bkgdorder <= 13)
372 m_bkgdorder = 12;
373 else
374 throw runtime_error("There is something wrong to set up FullprofPolynomial. ");
375 } else {
376 // order n will have n+1 parameters
377 if (m_bkgdorder == 0)
378 throw runtime_error("Polynomial and Chebyshev at least be order 0 (1 parameter). ");
379 --m_bkgdorder;
380 }
381}
382
383//=================================== Pattern Calculation & Minimizing
384//=======================
385
386//----------------------------------------------------------------------------------------------
394 // Generate domain and values vectors
395 const auto &vecX = m_dataWS->x(m_wsIndex).rawData();
396 std::vector<double> vecY(m_outputWS->y(CALDATAINDEX).size(), 0);
397
398 // Calculate diffraction pattern
399 Rfactor rfactor(-DBL_MAX, -DBL_MAX);
400 // FIXME - It should be a new ticket to turn on this option (use
401 // user-specified peak height)
402 bool useinputpeakheights = this->getProperty("UseInputPeakHeights");
403 if (useinputpeakheights)
404 g_log.warning("UseInputPeakHeights is temporarily turned off now. ");
405
406 // Set the parameters to LeBailFunction
407 map<string, double> profilemap = convertToDoubleMap(m_funcParameters);
408 m_lebailFunction->setProfileParameterValues(profilemap);
409
410 // Calculate peak intensities and diffraction pattern
411 vector<double> emptyvec;
412 bool resultphysical =
413 calculateDiffractionPattern(m_dataWS->x(m_wsIndex), m_dataWS->y(m_wsIndex), true, true, emptyvec, vecY, rfactor);
414 m_outputWS->mutableY(CALDATAINDEX) = vecY;
415
416 // Calculate background
417 m_outputWS->mutableY(INPUTBKGDINDEX) = m_lebailFunction->function(vecX, false, true);
418
420
421 // Set up output workspaces
423
424 // Calcualte individual peaks
425 bool ploteachpeak = this->getProperty("PlotIndividualPeaks");
426 g_log.information() << "Output individual peaks = " << ploteachpeak << ".\n";
427 if (ploteachpeak) {
428 for (size_t ipk = 0; ipk < m_lebailFunction->getNumberOfPeaks(); ++ipk) {
429 m_outputWS->mutableY(9 + ipk) = m_lebailFunction->calPeak(ipk, vecX, m_outputWS->y(9 + ipk).size());
430 }
431 }
432
433 // Record for output
434 Parameter par_rwp;
435 par_rwp.name = "Rwp";
436 par_rwp.curvalue = rfactor.Rwp;
437 m_funcParameters["Rwp"] = par_rwp;
438
439 g_log.notice() << "Rwp = " << rfactor.Rwp << ", Rp = " << rfactor.Rp << "\n";
440
441 if (!resultphysical) {
442 g_log.warning() << "Input parameters are unable to generate peaks that are physical."
443 << ".\n";
444 }
445}
446
447//==================================== Refine background
448//====================================
449//----------------------------------------------------------------------------------------------
458 // Set up class variables for background
459 m_bkgdParameterNames = m_backgroundFunction->getParameterNames();
463 m_roundBkgd = 0;
465 for (size_t i = 1; i < m_numberBkgdParameters; ++i) {
467 }
468
469 // 1. Generate domain and value
470 const auto &vecX = m_dataWS->x(m_wsIndex).rawData();
471 const auto &vecY = m_dataWS->y(m_wsIndex);
472 vector<double> valueVec(vecX.size(), 0);
473 size_t numpts = vecX.size();
474
475 API::FunctionDomain1DVector domain(vecX);
476 API::FunctionValues values(domain);
477
478 // 2. Calculate diffraction pattern
479 Rfactor currR(DBL_MAX, DBL_MAX);
480 m_backgroundFunction->function(domain, values);
481 vector<double> backgroundvalues = values.toVector();
482 for (size_t i = 0; i < numpts; ++i) {
483 m_outputWS->mutableY(INPUTPUREPEAKINDEX)[i] = m_dataWS->y(m_wsIndex)[i] - values[i];
484 }
485 m_outputWS->setSharedE(INPUTPUREPEAKINDEX, m_dataWS->sharedE(m_wsIndex));
486 map<string, double> parammap = convertToDoubleMap(m_funcParameters);
487 m_lebailFunction->setProfileParameterValues(parammap);
489 backgroundvalues, valueVec, currR);
490 Rfactor bestR = currR;
492 stringstream bufss;
493 bufss << "Starting background parameter ";
494 for (size_t i = 0; i < m_bestBkgdParams.size(); ++i)
495 bufss << "[" << i << "] = " << m_bestBkgdParams[i] << ", ";
496 bufss << ". Starting Rwp = " << currR.Rwp;
497 g_log.notice(bufss.str());
498
499 for (size_t istep = 0; istep < m_numMinimizeSteps; ++istep) {
500 // a) Store current setup
502
503 // b) Propose new values and evalulate
505 Rfactor newR(DBL_MAX, DBL_MAX);
506 m_backgroundFunction->function(domain, values);
507 backgroundvalues = values.toVector();
508 for (size_t i = 0; i < numpts; ++i) {
509 m_outputWS->mutableY(INPUTPUREPEAKINDEX)[i] = m_dataWS->y(m_wsIndex)[i] - values[i];
510 }
512 m_lebailFunction->setProfileParameterValues(parammap);
514 backgroundvalues, valueVec, newR);
515
516 g_log.information() << "[DBx800] New Rwp = " << newR.Rwp << ", Rp = " << newR.Rp << ".\n";
517
518 bool accept = acceptOrDeny(currR, newR);
519
520 // c) Process result
521 if (!accept) {
522 // Not accept. Restore original
524 } else {
525 // Accept
526 currR = newR;
527 if (newR.Rwp < bestR.Rwp) {
528 // Is it the best?
529 bestR = newR;
531
532 stringstream ss;
533 ss << "Temp best background parameter ";
534 for (size_t i = 0; i < m_bestBkgdParams.size(); ++i)
535 ss << "[" << i << "] = " << m_bestBkgdParams[i] << ", ";
536 g_log.information(ss.str());
537 }
538 }
539
540 // d) Progress
541 progress(static_cast<double>(istep) / static_cast<double>(m_numMinimizeSteps));
542 }
543
544 // 3. Recover the best
546
547 stringstream bufss1;
548 bufss1 << "Best background parameter ";
549 for (size_t i = 0; i < m_bkgdParameterStepVec.size(); ++i)
550 bufss1 << "[" << i << "] = " << m_backgroundFunction->getParameter(i) << ", ";
551 g_log.notice(bufss1.str());
552
553 Rfactor outputR(-DBL_MAX, -DBL_MAX);
554 m_backgroundFunction->function(domain, values);
555 backgroundvalues = values.toVector();
556 for (size_t i = 0; i < numpts; ++i) {
557 m_outputWS->mutableY(INPUTPUREPEAKINDEX)[i] = m_dataWS->y(m_wsIndex)[i] - values[i];
558 }
560 m_lebailFunction->setProfileParameterValues(parammap);
562 backgroundvalues, valueVec, outputR);
563
564 g_log.notice() << "[RefineBackground] Best Rwp = " << bestR.Rwp << ", vs. recovered best Rwp = " << outputR.Rwp
565 << ".\n";
566
567 // 4. Add data (0: experimental, 1: calcualted, 2: difference)
568 auto &vecY1 = m_outputWS->mutableY(1);
569 auto &vecY2 = m_outputWS->mutableY(2);
570 for (size_t i = 0; i < numpts; ++i) {
571 vecY1[i] = valueVec[i] + backgroundvalues[i];
572 vecY2[i] = vecY[i] - (valueVec[i] + backgroundvalues[i]);
573 }
574
575 // (3: peak without background, 4: input background)
576 // m_backgroundFunction->function(domain, values);
577 m_outputWS->mutableY(CALBKGDINDEX) = backgroundvalues;
578 m_outputWS->mutableY(CALPUREPEAKINDEX) = valueVec;
579
580 // 5. Output background to table workspace
581 auto outtablews = std::make_shared<TableWorkspace>();
582 outtablews->addColumn("str", "Name");
583 outtablews->addColumn("double", "Value");
584 outtablews->addColumn("double", "Error");
585
586 for (const auto &parname : m_bkgdParameterNames) {
587 double parvalue = m_backgroundFunction->getParameter(parname);
588
589 TableRow newrow = outtablews->appendRow();
590 newrow << parname << parvalue << 1.0;
591 }
592
593 setProperty("BackgroundParametersWorkspace", outtablews);
594}
595
596//----------------------------------------------------------------------------------------------
601void LeBailFit::storeBackgroundParameters(vector<double> &bkgdparamvec) {
602 for (size_t i = 0; i < m_numberBkgdParameters; ++i) {
603 bkgdparamvec[i] = m_backgroundFunction->getParameter(i);
604 }
605}
606
611void LeBailFit::recoverBackgroundParameters(const vector<double> &bkgdparamvec) {
612 for (size_t i = 0; i < m_numberBkgdParameters; ++i) {
613 m_backgroundFunction->setParameter(i, bkgdparamvec[i]);
614 }
615}
616
620 int iparam = m_roundBkgd % static_cast<int>(m_numberBkgdParameters);
621
622 double currvalue = m_backgroundFunction->getParameter(static_cast<int>(iparam));
623 double r = 2 * (static_cast<double>(rand()) / static_cast<double>(RAND_MAX) - 0.5);
624 double newvalue = currvalue + r * m_bkgdParameterStepVec[iparam];
625
626 g_log.information() << "[DBx804] Background " << iparam << " propose new value = " << newvalue << " from "
627 << currvalue << ".\n";
628
629 m_backgroundFunction->setParameter(static_cast<size_t>(iparam), newvalue);
630
631 ++m_roundBkgd;
632}
633
634//=================================== Set up the Le Bail Fit
635//================================
636//----------------------------------------------------------------------------------------------
641 // Generate Le Bail function
642 m_lebailFunction = std::make_shared<LeBailFunction>(LeBailFunction(m_peakType));
643
644 // Set up profile parameters
645 if (m_funcParameters.empty())
646 throw runtime_error("Function parameters must be set up by this point.");
647
648 map<string, double> pardblmap = convertToDoubleMap(m_funcParameters);
649 m_lebailFunction->setProfileParameterValues(pardblmap);
650
651 // Add peaks
652 if (!isEmpty(m_peakCentreTol)) {
653 const auto &vecx = m_dataWS->x(m_wsIndex);
654 m_lebailFunction->setPeakCentreTolerance(m_peakCentreTol, vecx.front(), vecx.back());
655 }
656
657 vector<vector<int>> vecHKL;
658 vector<pair<vector<int>, double>>::iterator piter;
659 for (piter = m_inputPeakInfoVec.begin(); piter != m_inputPeakInfoVec.end(); ++piter)
660 vecHKL.emplace_back(piter->first);
661 m_lebailFunction->addPeaks(vecHKL);
662
663 // Add background
666}
667
668//----------------------------------------------------------------------------------------------
674 // Process input property 'FitRegion' for range of data to fit/calculate
675 std::vector<double> fitrange = this->getProperty("FitRegion");
676
677 double tof_min, tof_max;
678 if (fitrange.empty()) {
679 tof_min = inpws->x(wsindex)[0];
680 tof_max = inpws->x(wsindex).back();
681 } else if (fitrange.size() == 2) {
682 tof_min = fitrange[0];
683 tof_max = fitrange[1];
684 } else {
685 g_log.warning() << "Input FitRegion has more than 2 entries. Using "
686 "default in stread.\n";
687
688 tof_min = inpws->x(wsindex)[0];
689 tof_max = inpws->x(wsindex).back();
690 }
691
692 // Crop workspace
693 auto cropalg = createChildAlgorithm("CropWorkspace", -1, -1, true);
694 cropalg->initialize();
695
696 cropalg->setProperty("InputWorkspace", inpws);
697 cropalg->setPropertyValue("OutputWorkspace", "MyData");
698 cropalg->setProperty("XMin", tof_min);
699 cropalg->setProperty("XMax", tof_max);
700
701 bool cropstatus = cropalg->execute();
702 if (!cropstatus) {
703 std::stringstream errmsg;
704 errmsg << "DBx309 Cropping workspace unsuccessful. Fatal Error. Quit!";
705 g_log.error() << errmsg.str() << "\n";
706 throw std::runtime_error(errmsg.str());
707 }
708
709 API::MatrixWorkspace_sptr cropws = cropalg->getProperty("OutputWorkspace");
710 if (!cropws) {
711 g_log.error("Unable to retrieve a Workspace2D object from ChildAlgorithm "
712 "CropWorkspace");
713 throw runtime_error("Unable to retrieve a Workspace2D object from "
714 "ChildAlgorithm CropWorkspace");
715 } else {
716 g_log.debug() << "DBx307: Cropped Workspace... Range From " << cropws->x(wsindex)[0] << " To "
717 << cropws->x(wsindex).back() << " of size " << cropws->x(wsindex).size() << "\n";
718 }
719
720 return cropws;
721}
722
723//================================= Import/Parse and Output
724//===================================
725//----------------------------------------------------------------------------------------------
729 // Peak type
730 m_peakType = getPropertyValue("PeakType");
731
732 // 1. Get input and perform some check
733 // a) Import data workspace and related, do crop
734 API::MatrixWorkspace_sptr inpWS = this->getProperty("InputWorkspace");
735
736 int tempindex = this->getProperty("WorkspaceIndex");
737 m_wsIndex = size_t(tempindex);
738
739 if (m_wsIndex >= inpWS->getNumberHistograms()) {
740 // throw if workspace index is not correct
741 stringstream errss;
742 errss << "Input WorkspaceIndex " << tempindex << " is out of boundary [0, " << inpWS->getNumberHistograms()
743 << "). ";
744 g_log.error(errss.str());
745 throw runtime_error(errss.str());
746 }
747
748 m_dataWS = this->cropWorkspace(inpWS, m_wsIndex);
749 m_startX = m_dataWS->x(0).front();
750 m_endX = m_dataWS->x(0).back();
751
752 // b) Minimizer
753 std::string minim = getProperty("Minimizer");
754 mMinimizer = minim;
755
756 // c) Peak parameters and related.
757 parameterWS = this->getProperty("InputParameterWorkspace");
758 reflectionWS = this->getProperty("InputHKLWorkspace");
759 mPeakRadius = this->getProperty("PeakRadius");
760
761 // d) Determine Functionality (function mode)
762 std::string function = this->getProperty("Function");
763 m_fitMode = FIT; // Default: LeBailFit
764 if (function == "Calculation") {
765 // peak calculation
767 } else if (function == "CalculateBackground") {
768 // automatic background points selection
770 } else if (function == "MonteCarlo") {
771 // Monte Carlo random walk refinement
773 } else if (function == "LeBailFit") {
774 // Le Bail Fit mode
775 m_fitMode = FIT;
776 } else if (function == "RefineBackground") {
777 // Refine background mode
779 } else {
780 stringstream errss;
781 errss << "Function mode " << function << " is not supported by LeBailFit().";
782 g_log.error(errss.str());
783 throw invalid_argument(errss.str());
784 }
785
786 m_dampingFactor = getProperty("Damping");
787
788 tempindex = getProperty("NumberMinimizeSteps");
789 if (tempindex > 0)
790 m_numMinimizeSteps = static_cast<size_t>(tempindex);
791 else {
793 stringstream errss;
794 errss << "Input number of random walk steps (" << m_numMinimizeSteps << ") cannot be less and equal to zero.";
795 g_log.error(errss.str());
796 throw invalid_argument(errss.str());
797 }
798
799 m_minimumPeakHeight = getProperty("MinimumPeakHeight");
800 m_indicatePeakHeight = getProperty("IndicationPeakHeight");
801
802 // Tolerate duplicated input peak or not?
803 m_tolerateInputDupHKL2Peaks = getProperty("AllowDegeneratedPeaks");
804
805 // Tolerance in peak positions to import peak
806 m_peakCentreTol = getProperty("ToleranceToImportPeak");
807}
808
809//----------------------------------------------------------------------------------------------
814 // 1. Check column orders
815 if (parameterWS->columnCount() < 3) {
816 g_log.error() << "Input parameter table workspace does not have enough "
817 "number of columns. "
818 << " Number of columns (Input =" << parameterWS->columnCount() << ") >= 3 as required.\n";
819 throw std::invalid_argument("Input parameter workspace is wrong. ");
820 } else {
821 g_log.information() << "[DB] Starting to parse instrument parameter table workspace " << parameterWS->getName()
822 << ".\n";
823 }
824
825 // 2. Import data to maps
826 size_t numrows = parameterWS->rowCount();
827 std::vector<std::string> colnames = parameterWS->getColumnNames();
828 size_t numcols = colnames.size();
829
830 std::map<std::string, double> tempdblmap;
831 std::map<std::string, std::string> tempstrmap;
832 std::map<std::string, double>::iterator dbliter;
833 std::map<string, string>::iterator striter;
834
835 std::string colname;
836 double dblvalue;
837 std::string strvalue;
838
839 for (size_t ir = 0; ir < numrows; ++ir) {
840 // a) Clear the map
841 tempdblmap.clear();
842 tempstrmap.clear();
843
844 // b) Get the row
845 API::TableRow trow = parameterWS->getRow(ir);
846
847 // c) Parse each term
848 for (size_t icol = 0; icol < numcols; ++icol) {
849 colname = colnames[icol];
850 if (colname != "FitOrTie" && colname != "Name") {
851 // double data
852 g_log.debug() << "Col-name = " << colname << ", ";
853 trow >> dblvalue;
854 g_log.debug() << "Value = " << dblvalue << ".\n";
855 ;
856 tempdblmap.emplace(colname, dblvalue);
857 } else {
858 // string data
859 g_log.debug() << "Col-name = " << colname << ", ";
860 trow >> strvalue;
861 strvalue.erase(std::find_if(strvalue.rbegin(), strvalue.rend(), std::not_fn(::isspace)).base(), strvalue.end());
862
863 g_log.debug() << "Value = " << strvalue << ".\n";
864 tempstrmap.emplace(colname, strvalue);
865 }
866 }
867
868 // d) Construct a Parameter instance
869 Parameter newparameter;
870 // i. name
871 striter = tempstrmap.find("Name");
872 if (striter != tempstrmap.end()) {
873 newparameter.name = striter->second;
874 } else {
875 std::stringstream errmsg;
876 errmsg << "Parameter (table) workspace " << parameterWS->getName()
877 << " does not contain column 'Name'. It is not a valid input. "
878 "Quit ";
879 g_log.error() << errmsg.str() << "\n";
880 throw std::invalid_argument(errmsg.str());
881 }
882
883 // ii. fit
884 striter = tempstrmap.find("FitOrTie");
885 if (striter != tempstrmap.end()) {
886 std::string fitortie = striter->second;
887 bool tofit = true;
888 if (fitortie.length() > 0) {
889 char fc = fitortie.c_str()[0];
890 if (fc == 't' || fc == 'T') {
891 tofit = false;
892 }
893 }
894 newparameter.fit = tofit;
895 } else {
896 std::stringstream errmsg;
897 errmsg << "Parameter (table) workspace " << parameterWS->getName()
898 << " does not contain column 'FitOrTie'. It is not a valid "
899 "input. Quit ";
900 g_log.error() << errmsg.str() << "\n";
901 throw std::invalid_argument(errmsg.str());
902 }
903
904 // iii. value
905 dbliter = tempdblmap.find("Value");
906 if (dbliter != tempdblmap.end()) {
907 newparameter.curvalue = dbliter->second;
908 } else {
909 std::stringstream errmsg;
910 errmsg << "Parameter (table) workspace " << parameterWS->getName()
911 << " does not contain column 'Value'. It is not a valid input. "
912 "Quit ";
913 g_log.error() << errmsg.str() << "\n";
914 throw std::invalid_argument(errmsg.str());
915 }
916
917 // iv. min
918 dbliter = tempdblmap.find("Min");
919 if (dbliter != tempdblmap.end()) {
920 newparameter.minvalue = dbliter->second;
921 } else {
922 newparameter.minvalue = -1.0E10;
923 }
924
925 // v. max
926 dbliter = tempdblmap.find("Max");
927 if (dbliter != tempdblmap.end()) {
928 newparameter.maxvalue = dbliter->second;
929 } else {
930 newparameter.maxvalue = 1.0E10;
931 }
932
933 // vi. stepsize
934 dbliter = tempdblmap.find("StepSize");
935 if (dbliter != tempdblmap.end()) {
936 newparameter.stepsize = dbliter->second;
937 } else {
938 newparameter.stepsize = 1.0;
939 }
940
941 // vii. error
942 newparameter.fiterror = 1.0E10;
943
944 // viii. some historical records
945 // FIXME : Consider to move parameter value's min/max to Monte Carlo table
946 newparameter.minrecordvalue = newparameter.maxvalue + 1.0;
947 newparameter.maxrecordvalue = newparameter.minvalue - 1.0;
948
949 m_funcParameters.emplace(newparameter.name, newparameter);
950 m_origFuncParameters.emplace(newparameter.name, newparameter.curvalue);
951
952 g_log.information() << "Inserting Parameter " << newparameter.name << " = " << newparameter.curvalue << ".\n";
953
954 if (newparameter.fit) {
955 g_log.debug() << "[Input]: " << newparameter.name << ": value = " << newparameter.curvalue << " Range: ["
956 << newparameter.minvalue << ", " << newparameter.maxvalue
957 << "], MC Step = " << newparameter.stepsize << ", Fit? = " << newparameter.fit << "\n";
958 }
959 } // ENDFOR rows in Table
960
961 g_log.information() << "[DB]: Successfully Imported Peak Parameters TableWorkspace " << parameterWS->getName()
962 << ". Imported " << m_funcParameters.size() << " parameters. "
963 << "\n";
964}
965
966//----------------------------------------------------------------------------------------------
972 // 1. Check column orders
973 std::vector<std::string> colnames = reflectionWS->getColumnNames();
974 if (colnames.size() < 3) {
975 g_log.error() << "Input parameter table workspace does not have enough "
976 "number of columns. "
977 << " Number of columns = " << colnames.size() << " < 3 as required.\n";
978 throw std::runtime_error("Input parameter workspace is wrong. ");
979 }
980 if (colnames[0] != "H" || colnames[1] != "K" || colnames[2] != "L") {
981 stringstream errss;
982 errss << "Input Bragg peak parameter TableWorkspace does not have the "
983 "columns in order. "
984 << "It must be H, K, L. for the first 3 columns.";
985 g_log.error(errss.str());
986 throw std::runtime_error(errss.str());
987 }
988
989 // Has peak height?
990 bool hasPeakHeight = false;
991 if (colnames.size() >= 4 && colnames[3] == "PeakHeight") {
992 // Has a column for peak height
993 hasPeakHeight = true;
994 }
995
996 /* FIXME This section is disabled. It should be a new ticket to turn on this
997 option.
998 bool userexcludepeaks = false;
999 if (colnames.size() >= 5 && colnames[4] == "Include/Exclude")
1000 {
1001 userexcludepeaks = true;
1002 }
1003 */
1004
1005 // 2. Import data to maps
1006 int h, k, l;
1007
1008 size_t numrows = reflectionWS->rowCount();
1009 for (size_t ir = 0; ir < numrows; ++ir) {
1010 // 1. Get from table row
1011 API::TableRow trow = reflectionWS->getRow(ir);
1012 trow >> h >> k >> l;
1013
1014 // 3. Insert related data structure
1015 std::vector<int> hkl;
1016 hkl.emplace_back(h);
1017 hkl.emplace_back(k);
1018 hkl.emplace_back(l);
1019
1020 // optional peak height
1021 double peakheight = 1.0;
1022 if (hasPeakHeight) {
1023 trow >> peakheight;
1024 }
1025
1026 m_inputPeakInfoVec.emplace_back(hkl, peakheight);
1027 } // ENDFOR row
1028
1029 g_log.information() << "Imported HKL TableWorkspace. Size of Rows = " << numrows << "\n";
1030}
1031
1032//----------------------------------------------------------------------------------------------
1036void LeBailFit::parseBackgroundTableWorkspace(const TableWorkspace_sptr &bkgdparamws, vector<string> &bkgdparnames,
1037 vector<double> &bkgdorderparams) {
1038 g_log.debug() << "DB1105A Parsing background TableWorkspace.\n";
1039
1040 // Clear (output) map
1041 bkgdorderparams.clear();
1042
1043 // Check background parameter table workspace
1044 std::vector<std::string> colnames = bkgdparamws->getColumnNames();
1045 if (colnames.size() < 2) {
1046 stringstream errss;
1047 errss << "Input background parameter table workspace " << bkgdparamws->getName() << " has only " << colnames.size()
1048 << " columns, which is fewer than 2 columns as required. ";
1049 g_log.error(errss.str());
1050 throw runtime_error(errss.str());
1051 } else {
1052 if (!(colnames[0].starts_with("Name") && colnames[1].starts_with("Value"))) {
1053 // Column 0 and 1 must be Name and Value (at least started with)
1054 stringstream errss;
1055 errss << "Input parameter table workspace have wrong column definition. "
1056 << "Column 0 should be Name. And column 1 should be Value. "
1057 "Current input is: \n";
1058 for (size_t i = 0; i < 2; ++i)
1059 errss << "Column " << i << ": " << colnames[0] << "\n";
1060 g_log.error(errss.str());
1061 throw runtime_error(errss.str());
1062 }
1063 }
1064
1065 // Parse input table workspace to a map. Valid parameter names must start
1066 // with A.
1067 std::map<std::string, double> parmap;
1068 for (size_t ir = 0; ir < bkgdparamws->rowCount(); ++ir) {
1069 API::TableRow row = bkgdparamws->getRow(ir);
1070 std::string parname;
1071 double parvalue;
1072 row >> parname >> parvalue;
1073
1074 // Remove extra white spaces
1075 boost::algorithm::trim(parname);
1076
1077 if (!parname.empty() && (parname[0] == 'A' || parname == "Bkpos")) {
1078 // Insert parameter name starting with A or Bkpos (special case for
1079 // FullprofPolynomial)
1080 parmap.emplace(parname, parvalue);
1081 }
1082 }
1083
1084 // Add pair to vector
1085 bkgdparnames.reserve(parmap.size());
1086 bkgdorderparams.reserve(parmap.size());
1087
1088 std::map<std::string, double>::iterator mit;
1089 for (mit = parmap.begin(); mit != parmap.end(); ++mit) {
1090 std::string parname = mit->first;
1091 double parvalue = mit->second;
1092 bkgdparnames.emplace_back(parname);
1093 bkgdorderparams.emplace_back(parvalue);
1094 }
1095
1096 // Debug output
1097 std::stringstream msg;
1098 msg << "Finished importing background TableWorkspace. "
1099 << "Background Order = " << bkgdorderparams.size() << ": ";
1100 for (size_t iod = 0; iod < bkgdorderparams.size(); ++iod) {
1101 msg << bkgdparnames[iod] << " = " << bkgdorderparams[iod] << "; ";
1102 }
1103 g_log.information(msg.str());
1104}
1105
1106//----------------------------------------------------------------------------------------------
1112 // Create peaks workspace
1114
1115 // Add columns to table/peak workspace
1116 peakWS->addColumn("int", "H");
1117 peakWS->addColumn("int", "K");
1118 peakWS->addColumn("int", "L");
1119 peakWS->addColumn("double", "Height");
1120 peakWS->addColumn("double", "TOF_h");
1121 peakWS->addColumn("double", "Alpha");
1122 peakWS->addColumn("double", "Beta");
1123 peakWS->addColumn("double", "Sigma2");
1124 peakWS->addColumn("double", "Gamma");
1125 peakWS->addColumn("double", "FWHM");
1126 peakWS->addColumn("int", "PeakGroup");
1127 peakWS->addColumn("double", "Chi^2");
1128 peakWS->addColumn("str", "FitStatus");
1129
1130 // Add each peak in LeBailFunction to peak/table workspace
1131 for (size_t ipk = 0; ipk < m_lebailFunction->getNumberOfPeaks(); ++ipk) {
1132
1133 // Miller index and peak parameters
1135
1136 int h, k, l;
1137 peak->getMillerIndex(h, k, l);
1138 double tof_h = peak->centre();
1139 double height = peak->height();
1140 double alpha = peak->getPeakParameter("Alpha");
1141 double beta = peak->getPeakParameter("Beta");
1142 double sigma2 = peak->getPeakParameter("Sigma2");
1143 double gamma = peak->getPeakParameter("Gamma");
1144 double fwhm = peak->fwhm();
1145
1146 // New row
1147 API::TableRow newrow = peakWS->appendRow();
1148 newrow << h << k << l << height << tof_h << alpha << beta << sigma2 << gamma << fwhm << -1 << -1.0 << "N/A";
1149
1150 if (tof_h < 0) {
1151 stringstream errss;
1152 errss << "Peak (" << h << ", " << k << ", " << l << "): TOF_h (=" << tof_h << ") is NEGATIVE!";
1153 g_log.error(errss.str());
1154 }
1155 }
1156
1157 // Set property
1158 this->setProperty("OutputPeaksWorkspace", peakWS);
1159}
1160
1161//----------------------------------------------------------------------------------------------
1167void LeBailFit::exportInstrumentParameterToTable(const std::map<std::string, Parameter> &parammap) {
1168 // 1. Create table workspace
1170
1171 tablews = new DataObjects::TableWorkspace();
1172 DataObjects::TableWorkspace_sptr parameterws(tablews);
1173
1174 tablews->addColumn("str", "Name");
1175 tablews->addColumn("double", "Value");
1176 tablews->addColumn("str", "FitOrTie");
1177 tablews->addColumn("double", "chi^2");
1178 tablews->addColumn("double", "Min");
1179 tablews->addColumn("double", "Max");
1180 tablews->addColumn("double", "StepSize");
1181 tablews->addColumn("double", "StartValue");
1182 tablews->addColumn("double", "Diff");
1183
1184 // 2. Add profile parameter value
1185 std::map<std::string, Parameter>::const_iterator paramiter;
1186 std::map<std::string, double>::iterator opiter;
1187 for (paramiter = parammap.begin(); paramiter != parammap.end(); ++paramiter) {
1188 std::string parname = paramiter->first;
1189 if (parname != "Height") {
1190 // Export every parameter except "Height"
1191
1192 // a) current value
1193 double parvalue = paramiter->second.curvalue;
1194
1195 // b) fit or tie?
1196 char fitortie = 't';
1197 if (paramiter->second.fit) {
1198 fitortie = 'f';
1199 }
1200 std::stringstream ss;
1201 ss << fitortie;
1202 std::string fit_tie = ss.str();
1203
1204 // c) starting value
1205 opiter = m_origFuncParameters.find(parname);
1206 double origparvalue = -1.0E100;
1207 if (opiter != m_origFuncParameters.end()) {
1208 origparvalue = opiter->second;
1209 }
1210 double diff = origparvalue - parvalue;
1211
1212 // d. (standard) error
1213 double paramerror = paramiter->second.fiterror;
1214
1215 // e. create the row
1216 double min = paramiter->second.minvalue;
1217 double max = paramiter->second.maxvalue;
1218 double step = paramiter->second.stepsize;
1219
1220 API::TableRow newparam = tablews->appendRow();
1221 newparam << parname << parvalue << fit_tie << paramerror << min << max << step << origparvalue << diff;
1222 } // ENDIF
1223 }
1224
1225 // 3. Add chi^2
1227 // Impossible mode
1228 throw runtime_error("Impossible to have this situation happen. Flag 541.");
1229 } else if (!m_inputParameterPhysical) {
1230 // Input instrument profile parameters are not physical
1231 m_lebailCalChi2 = DBL_MAX;
1232 m_lebailFitChi2 = DBL_MAX;
1233 }
1234
1235 if (m_fitMode == FIT) {
1236 // Do this for FIT mode only
1237 API::TableRow fitchi2row = tablews->appendRow();
1238 fitchi2row << "FitChi2" << m_lebailFitChi2 << "t" << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0;
1239 API::TableRow chi2row = tablews->appendRow();
1240 chi2row << "Chi2" << m_lebailCalChi2 << "t" << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0;
1241 }
1242
1243 // 4. Add to output peroperty
1244 setProperty("OutputParameterWorkspace", parameterws);
1245}
1246
1247//----------------------------------------------------------------------------------------------
1263 // 1. Determine number of output spectra
1264 size_t nspec = 9;
1265
1266 if (m_fitMode == CALCULATION) {
1267 bool plotindpeak = this->getProperty("PlotIndividualPeaks");
1268 if (plotindpeak) {
1269 nspec += m_lebailFunction->getNumberOfPeaks();
1270 g_log.information() << "Number of peaks to add to output data = " << m_lebailFunction->getNumberOfPeaks()
1271 << ".\n";
1272 }
1273 }
1274
1275 // 2. Create workspace2D and set the data to spectrum 0 (common among all)
1276 size_t nbinx = m_dataWS->x(m_wsIndex).size();
1277 size_t nbiny = m_dataWS->y(m_wsIndex).size();
1278 m_outputWS = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
1279 API::WorkspaceFactory::Instance().create("Workspace2D", nspec, nbinx, nbiny));
1280
1281 // 3. Add values
1282 // All X.
1283 for (size_t j = 0; j < m_outputWS->getNumberHistograms(); ++j)
1284 m_outputWS->setSharedX(j, m_dataWS->sharedX(m_wsIndex));
1285
1286 // Observation
1287 m_outputWS->setSharedY(OBSDATAINDEX, m_dataWS->sharedY(m_wsIndex));
1288 m_outputWS->setSharedE(OBSDATAINDEX, m_dataWS->sharedE(m_wsIndex));
1289
1290 // 4. Set axis
1291 m_outputWS->getAxis(0)->setUnit("TOF");
1292
1293 auto tAxis = std::make_unique<API::TextAxis>(nspec);
1294 tAxis->setLabel(0, "Data");
1295 tAxis->setLabel(1, "Calc");
1296 tAxis->setLabel(2, "Diff");
1297 tAxis->setLabel(3, "CalcNoBkgd");
1298 tAxis->setLabel(4, "OutBkgd");
1299 tAxis->setLabel(5, "InpCalc");
1300 tAxis->setLabel(6, "InBkgd");
1301 tAxis->setLabel(7, "DataNoBkgd");
1302 tAxis->setLabel(8, "SmoothedBkgd");
1303
1304 if (m_fitMode == CALCULATION) {
1305 // Set the single peak labels
1306 for (size_t i = 0; i < (nspec - 9); ++i) {
1307 std::stringstream ss;
1308 ss << "Peak_" << i;
1309 tAxis->setLabel(9 + i, ss.str());
1310 }
1311 }
1312
1313 m_outputWS->replaceAxis(1, std::move(tAxis));
1314}
1315
1316// ====================================== Random Walk Suite
1317// ====================================
1318//----------------------------------------------------------------------------------------------
1324void LeBailFit::execRandomWalkMinimizer(size_t maxcycles, map<string, Parameter> &parammap) {
1325 // Set up random walk parameters
1326 const auto &vecX = m_dataWS->x(m_wsIndex);
1327 const auto &vecInY = m_dataWS->y(m_wsIndex);
1328
1329 const auto &domain = m_dataWS->x(m_wsIndex).rawData();
1330 std::vector<double> vecCalPurePeaks(domain.size(), 0.0);
1331
1332 // Strategy and map
1333 TableWorkspace_sptr mctablews = getProperty("MCSetupWorkspace");
1334 if (mctablews) {
1336 } else {
1338 }
1339 // Walking style/algorithm
1340 bool usedrunkenwalk = getProperty("DrunkenWalk");
1341 if (usedrunkenwalk)
1343 else
1345 // Annealing temperature
1346 m_Temperature = getProperty("AnnealingTemperature");
1347 if (m_Temperature < 0)
1349 m_useAnnealing = getProperty("UseAnnealing");
1350 // Random seed
1351 int randomseed = getProperty("RandomSeed");
1352
1353 // R-factors used for MC procedure
1354 Rfactor startR(-DBL_MAX, -DBL_MAX);
1355
1356 // Process background to make a pure peak spectrum in output workspace
1357 HistogramY vecBkgd = m_lebailFunction->function(vecX.rawData(), false, true);
1358 m_outputWS->mutableY(INPUTBKGDINDEX) = vecBkgd;
1359 m_outputWS->mutableY(INPUTPUREPEAKINDEX) = vecInY - vecBkgd;
1360
1361 // Calcualte starting Rwp and etc
1362 const auto &vecPurePeak = m_outputWS->y(INPUTPUREPEAKINDEX);
1363
1364 map<string, double> pardblmap = convertToDoubleMap(parammap);
1365 m_lebailFunction->setProfileParameterValues(pardblmap);
1366 bool startvaluevalid = calculateDiffractionPattern(vecX, vecPurePeak, false, false, vecBkgd, vecCalPurePeaks, startR);
1367 if (!startvaluevalid) {
1368 // Throw exception if starting values are not valid for all
1369 throw runtime_error("Starting value of instrument profile parameters can "
1370 "generate peaks with"
1371 " unphyiscal parameters values.");
1372 }
1373
1374 doMarkovChain(parammap, vecX, vecPurePeak, vecBkgd.rawData(), maxcycles, startR, randomseed);
1375
1376 // 5. Sum up: retrieve the best result from class variable: m_bestParameters
1377 Rfactor finalR(-DBL_MAX, -DBL_MAX);
1378 map<string, double> bestparams = convertToDoubleMap(m_bestParameters);
1379 m_lebailFunction->setProfileParameterValues(bestparams);
1380 calculateDiffractionPattern(vecX, vecPurePeak, false, false, vecBkgd, vecCalPurePeaks, finalR);
1381
1382 auto &vecCalY = m_outputWS->mutableY(CALDATAINDEX);
1383 auto &vecDiff = m_outputWS->mutableY(DATADIFFINDEX);
1384 auto &vecCalPurePeak = m_outputWS->mutableY(CALPUREPEAKINDEX);
1385 auto &vecCalBkgd = m_outputWS->mutableY(CALBKGDINDEX);
1386 // Calculated (refined) data
1387 vecCalY = vecCalPurePeaks;
1388 vecCalY += vecBkgd;
1389 // Diff
1390 vecDiff = vecInY - vecCalY;
1391 // Calcualted without background (pure peaks)
1392 vecCalPurePeak = vecCalPurePeaks;
1393 // Different between calculated peaks and raw data
1394 vecCalBkgd = vecInY - vecCalPurePeaks;
1395
1396 // c) Apply the best parameters to param
1398 Parameter par_rwp;
1399 par_rwp.name = "Rwp";
1400 par_rwp.curvalue = m_bestRwp;
1401 parammap["Rwp"] = par_rwp;
1402} // Main Exec MC
1403
1404//----------------------------------------------------------------------------------------------
1407void LeBailFit::doMarkovChain(const map<string, Parameter> &parammap, const Mantid::HistogramData::HistogramX &vecX,
1408 const Mantid::HistogramData::HistogramY &vecPurePeak, const vector<double> &vecBkgd,
1409 size_t maxcycles, const Rfactor &startR, int randomseed) {
1410
1411 // Rfactors in loop
1412 Rfactor currR(-DBL_MAX, -DBL_MAX), newR(-DBL_MAX, -DBL_MAX);
1413 // parameter map for newly proposed values
1414 map<string, Parameter> mapCurrParameter = parammap;
1415 map<string, Parameter> newparammap = mapCurrParameter;
1416 // Output vector from
1417 std::vector<double> veccalpurepeaks(vecX.size(), 0.0);
1418
1419 // Set starting parameters
1420 currR = startR;
1421 m_bestRwp = currR.Rwp + EPSILON;
1422 m_bestRp = currR.Rp + EPSILON;
1423 bookKeepBestMCResult(mapCurrParameter, vecBkgd, currR, 0);
1424
1425 g_log.notice() << "[MC-Start] Random-walk Starting Rwp = " << currR.Rwp << ", Rp = " << currR.Rp << "\n";
1426
1427 // Random walk loops
1428 // generate some MC trace structure
1429 vector<double> vecIndex(maxcycles + 1);
1430 vector<Rfactor> vecR(maxcycles + 1);
1431 size_t numinvalidmoves = 0;
1432 size_t numacceptance = 0;
1433 bool prevcyclebetterR = true;
1434
1435 // Annealing record
1436 int numRecentAcceptance = 0;
1437 int numRecentSteps = 0;
1438 int numConsecutiveInvalid = 0;
1439
1440 // FIXME - It seems that annealing cannot work together with reset invalid if
1441 // in the region of invalid.
1442 // Need to write down the procedure to think it over.
1443 int numMaxConsecutiveInvalid = 5;
1444 int m_numStepsCheckTemperature = 10;
1445
1446 // Loop start
1447 srand(randomseed);
1448
1449 for (size_t icycle = 1; icycle <= maxcycles; ++icycle) {
1450 // Refine parameters (for all parameters in turn) to data with background
1451 // removed
1452 for (const auto &MCGroup : m_MCGroups) {
1453 // Propose new value for ONE AND ONLY ONE Monte Carlo parameter group
1454 /*
1455 int igroup = giter->first; // group id
1456 g_log.debug() << "BigTrouble: Group " << igroup << "\n";
1457 */
1458 bool hasnewvalues = proposeNewValues(MCGroup.second, currR, mapCurrParameter, newparammap, prevcyclebetterR);
1459
1460 if (!hasnewvalues) {
1461 // No parameter to have value updated in this MC group. Skip evaluation
1462 // of LeBail function.
1463 continue;
1464 }
1465
1466 // Evaluate LeBail function
1467 map<string, double> newpardblmap = convertToDoubleMap(newparammap);
1468 m_lebailFunction->setProfileParameterValues(newpardblmap);
1469 bool validparams = calculateDiffractionPattern(vecX, vecPurePeak, false, false, vecBkgd, veccalpurepeaks, newR);
1470 g_log.debug() << "[Calculation] Rwp = " << newR.Rwp << ", Rp = " << newR.Rp << ".\n";
1471
1472 // Determine whether to take the change or not
1473 bool acceptchange;
1474 if (!validparams) {
1475 ++numinvalidmoves;
1476 acceptchange = false;
1477 prevcyclebetterR = false;
1478 ++numConsecutiveInvalid;
1479 } else {
1480 acceptchange = acceptOrDeny(currR, newR);
1481
1482 prevcyclebetterR = newR.Rwp < currR.Rwp;
1483 }
1484
1485 g_log.debug() << "[DBx317] Step " << icycle << ": New Rwp = " << setprecision(10) << newR.Rwp
1486 << ", Rp = " << setprecision(5) << newR.Rp << "; Accepted = " << acceptchange
1487 << "; Proposed parameters valid =" << validparams << "\n";
1488
1489 // Apply change and book keeping
1490 if (acceptchange) {
1491 // Apply the change to current
1492 applyParameterValues(newparammap, mapCurrParameter);
1493 currR = newR;
1494
1495 // All tim ebest
1496 // FIXME - [RPRWP] Use Rp now
1497 if (currR.Rwp < m_bestRwp) {
1498 // Book keep the best
1499 bookKeepBestMCResult(mapCurrParameter, vecBkgd, currR, icycle);
1500 }
1501 // FIXME - After determining to use Rp or Rwp, this should be got into
1502 // bookKeepBestMCResult
1503 if (currR.Rp < m_bestRp)
1504 m_bestRp = currR.Rp;
1505 if (currR.Rwp < m_bestRwp)
1506 m_bestRwp = currR.Rwp;
1507
1508 // Statistic
1509 ++numacceptance;
1510 ++numRecentAcceptance;
1511 }
1512 ++numRecentSteps;
1513
1514 // Annealing or start over
1515 if (numConsecutiveInvalid >= numMaxConsecutiveInvalid) {
1516 // Exceeds the limit of consecutive invalid proposed new values. Start
1517 // over
1518 mapCurrParameter = m_bestParameters;
1519
1520 // Reset counters
1521 numConsecutiveInvalid = 0;
1522 numRecentAcceptance = 0;
1523 numRecentSteps = 0;
1524
1525 } else if (m_useAnnealing) {
1526 // Annealing: change temperature to tune the acceptance rate
1527 // FIXME : Here are some magic numbers
1528 if (numRecentSteps == m_numStepsCheckTemperature) {
1529 // i. Change temperature
1530 if (numRecentAcceptance <= 2) {
1531 m_Temperature *= 2.0;
1532 } else if (numRecentAcceptance >= 8) {
1533 m_Temperature /= 2.0;
1534 }
1535 // ii Reset counters
1536 numRecentAcceptance = 0;
1537 numRecentSteps = 0;
1538 }
1539 }
1540
1541 // e) Debug output?
1542
1543 } // END FOR Group
1544
1545 // v. Improve the background
1546 // FIXME - [RPRWP] Use Rp now
1547 if (currR.Rwp < m_bestRwp) {
1548 // FIXME - Fit background is disabled at this moment
1549 // fitBackground(m_wsIndex, domainB, valuesB, background);
1550 }
1551
1552 // vi. Record some information
1553 vecIndex[icycle] = static_cast<double>(icycle);
1554 if (currR.Rwp < 1.0E5)
1555 vecR[icycle] = currR;
1556 else {
1557 Rfactor dum(-1, -1);
1558 vecR[icycle] = dum;
1559 }
1560
1561 // vii. progress
1562 if (icycle % 10 == 0)
1563 progress(double(icycle) / double(maxcycles));
1564
1565 } // ENDFOR MC Cycles
1566
1567 progress(1.0);
1568
1569 // a) Summary output
1570 g_log.notice() << "[SUMMARY] Random-walk R-factor: Best step @ " << m_bestMCStep
1571 << ", Acceptance ratio = " << double(numacceptance) / double(maxcycles * m_numMCGroups) << ".\n"
1572 << "Rwp: Starting = " << startR.Rwp << ", Best = " << m_bestRwp << ", Ending = " << currR.Rwp << "\n"
1573 << "Rp : Starting = " << startR.Rp << ", Best = " << m_bestRp << ", Ending = " << currR.Rp << "\n";
1574
1575 map<string, Parameter>::iterator mapiter;
1576 for (mapiter = mapCurrParameter.begin(); mapiter != mapCurrParameter.end(); ++mapiter) {
1577 const Parameter &param = mapiter->second;
1578 if (param.fit) {
1579 g_log.notice() << setw(10) << param.name << "\t: Average Stepsize = " << setw(10) << setprecision(5)
1580 << param.sumstepsize / double(maxcycles) << ", Max Step Size = " << setw(10) << setprecision(5)
1581 << param.maxabsstepsize << ", Number of Positive Move = " << setw(4) << param.numpositivemove
1582 << ", Number of Negative Move = " << setw(4) << param.numnegativemove
1583 << ", Number of No Move = " << setw(4) << param.numnomove << ", Minimum tried value = " << setw(4)
1584 << param.minrecordvalue << ", Maximum tried value = " << setw(4) << param.maxrecordvalue << "\n";
1585 }
1586 }
1587 g_log.notice() << "Number of invalid proposed moves = " << numinvalidmoves << "\n";
1588
1589 // b) Export trace of R
1590 stringstream filenamess;
1591 filenamess << "r_trace_" << vecR.size() << ".dat";
1592 writeRfactorsToFile(vecIndex, vecR, filenamess.str());
1593}
1594
1595//----------------------------------------------------------------------------------------------
1600 g_log.information("Set up random walk strategy from table.");
1601
1602 // Scan the table
1603 size_t numrows = tablews->rowCount();
1604 for (size_t i = 0; i < numrows; ++i) {
1605 // 1. Get a row and pass out
1606 TableRow temprow = tablews->getRow(i);
1607 string parname;
1608 double a0, a1;
1609 int nonnegative, group;
1610
1611 temprow >> parname >> a0 >> a1 >> nonnegative >> group;
1612
1613 // 2. MC group
1614 map<int, vector<string>>::iterator giter;
1615 giter = m_MCGroups.find(group);
1616 if (giter != m_MCGroups.end()) {
1617 giter->second.emplace_back(parname);
1618 } else {
1619 // First instance in the new group.
1620 m_MCGroups.emplace(group, std::initializer_list<std::string>{parname});
1621 }
1622
1623 // 3. Set up MC parameters, A0, A1, non-negative
1624 auto piter = m_funcParameters.find(parname);
1625 if (piter != m_funcParameters.end()) {
1626 piter->second.mcA0 = a0;
1627 piter->second.mcA1 = a1;
1628 piter->second.nonnegative = (nonnegative != 0);
1629 }
1630 }
1631
1633
1634 // 4. Reset
1635 map<string, Parameter>::iterator mapiter;
1636 for (mapiter = m_funcParameters.begin(); mapiter != m_funcParameters.end(); ++mapiter) {
1637 mapiter->second.movedirection = 1;
1638 mapiter->second.sumstepsize = 0.0;
1639 mapiter->second.numpositivemove = 0;
1640 mapiter->second.numnegativemove = 0;
1641 mapiter->second.numnomove = 0;
1642 mapiter->second.maxabsstepsize = -0.0;
1643 }
1644}
1645
1646//----------------------------------------------------------------------------------------------
1650 g_log.information("Set up random walk strategy from build-in. ");
1651
1652 stringstream dboutss;
1653 dboutss << "Monte Carlo minimizer refines: ";
1654
1655 // 1. Monte Carlo groups
1656 // a. Instrument gemetry
1657 vector<string> geomparams;
1658 addParameterToMCMinimize(geomparams, "Dtt1");
1659 addParameterToMCMinimize(geomparams, "Dtt1t");
1660 addParameterToMCMinimize(geomparams, "Dtt2t");
1661 addParameterToMCMinimize(geomparams, "Zero");
1662 addParameterToMCMinimize(geomparams, "Zerot");
1663 addParameterToMCMinimize(geomparams, "Width");
1664 addParameterToMCMinimize(geomparams, "Tcross");
1665 m_MCGroups.emplace(0, geomparams);
1666
1667 dboutss << "Geometry parameters: ";
1668 for (const auto &geomparam : geomparams)
1669 dboutss << geomparam << "\t\t";
1670 dboutss << "\n";
1671
1672 // b. Alphas
1673 vector<string> alphs;
1674 addParameterToMCMinimize(alphs, "Alph0");
1675 addParameterToMCMinimize(alphs, "Alph1");
1676 addParameterToMCMinimize(alphs, "Alph0t");
1677 addParameterToMCMinimize(alphs, "Alph1t");
1678 m_MCGroups.emplace(1, alphs);
1679
1680 dboutss << "Alpha parameters";
1681 for (const auto &alph : alphs)
1682 dboutss << alph << "\t\t";
1683 dboutss << "\n";
1684
1685 // c. Beta
1686 vector<string> betas;
1687 addParameterToMCMinimize(betas, "Beta0");
1688 addParameterToMCMinimize(betas, "Beta1");
1689 addParameterToMCMinimize(betas, "Beta0t");
1690 addParameterToMCMinimize(betas, "Beta1t");
1691 m_MCGroups.emplace(2, betas);
1692
1693 dboutss << "Beta parameters";
1694 for (const auto &beta : betas)
1695 dboutss << beta << "\t\t";
1696 dboutss << "\n";
1697
1698 // d. Sig
1699 vector<string> sigs;
1700 addParameterToMCMinimize(sigs, "Sig0");
1701 addParameterToMCMinimize(sigs, "Sig1");
1702 addParameterToMCMinimize(sigs, "Sig2");
1703 m_MCGroups.emplace(3, sigs);
1704
1705 dboutss << "Sig parameters";
1706 for (const auto &sig : sigs)
1707 dboutss << sig << "\t\t";
1708 dboutss << "\n";
1709
1710 g_log.notice(dboutss.str());
1711
1712 m_numMCGroups = m_MCGroups.size();
1713
1714 // 2. Dictionary for each parameter for non-negative, mcX0, mcX1
1715 // a) Sig0, Sig1, Sig2
1716 for (const auto &parname : sigs) {
1717 m_funcParameters[parname].mcA0 = 2.0;
1718 m_funcParameters[parname].mcA1 = 1.0;
1719 m_funcParameters[parname].nonnegative = true;
1720 }
1721
1722 // b) Alpha
1723 for (const auto &parname : alphs) {
1724 m_funcParameters[parname].mcA1 = 1.0;
1725 m_funcParameters[parname].nonnegative = false;
1726 }
1727 m_funcParameters["Alph0"].mcA0 = 0.05;
1728 m_funcParameters["Alph1"].mcA0 = 0.02;
1729 m_funcParameters["Alph0t"].mcA0 = 0.1;
1730 m_funcParameters["Alph1t"].mcA0 = 0.05;
1731
1732 // c) Beta
1733 for (const auto &parname : betas) {
1734 m_funcParameters[parname].mcA1 = 1.0;
1735 m_funcParameters[parname].nonnegative = false;
1736 }
1737 m_funcParameters["Beta0"].mcA0 = 0.5;
1738 m_funcParameters["Beta1"].mcA0 = 0.05;
1739 m_funcParameters["Beta0t"].mcA0 = 0.5;
1740 m_funcParameters["Beta1t"].mcA0 = 0.05;
1741
1742 // d) Geometry might be more complicated
1743 m_funcParameters["Width"].mcA0 = 0.0;
1744 m_funcParameters["Width"].mcA1 = 0.1;
1745 m_funcParameters["Width"].nonnegative = true;
1746
1747 m_funcParameters["Tcross"].mcA0 = 0.0;
1748 m_funcParameters["Tcross"].mcA1 = 1.0;
1749 m_funcParameters["Tcross"].nonnegative = true;
1750
1751 m_funcParameters["Zero"].mcA0 = 5.0; // 5.0
1752 m_funcParameters["Zero"].mcA1 = 0.0;
1753 m_funcParameters["Zero"].nonnegative = false;
1754
1755 m_funcParameters["Zerot"].mcA0 = 5.0; // 5.0
1756 m_funcParameters["Zerot"].mcA1 = 0.0;
1757 m_funcParameters["Zerot"].nonnegative = false;
1758
1759 m_funcParameters["Dtt1"].mcA0 = 5.0; // 20.0
1760 m_funcParameters["Dtt1"].mcA1 = 0.0;
1761 m_funcParameters["Dtt1"].nonnegative = true;
1762
1763 m_funcParameters["Dtt1t"].mcA0 = 5.0; // 20.0
1764 m_funcParameters["Dtt1t"].mcA1 = 0.0;
1765 m_funcParameters["Dtt1t"].nonnegative = true;
1766
1767 m_funcParameters["Dtt2t"].mcA0 = 0.1;
1768 m_funcParameters["Dtt2t"].mcA1 = 1.0;
1769 m_funcParameters["Dtt2t"].nonnegative = false;
1770
1771 // 4. Reset
1772 map<string, Parameter>::iterator mapiter;
1773 for (mapiter = m_funcParameters.begin(); mapiter != m_funcParameters.end(); ++mapiter) {
1774 mapiter->second.movedirection = 1;
1775 mapiter->second.sumstepsize = 0.0;
1776 mapiter->second.numpositivemove = 0;
1777 mapiter->second.numnegativemove = 0;
1778 mapiter->second.numnomove = 0;
1779 mapiter->second.maxabsstepsize = -0.0;
1780 }
1781}
1782
1783//----------------------------------------------------------------------------------------------
1791void LeBailFit::addParameterToMCMinimize(vector<string> &parnamesforMC, const string &parname) {
1792 map<string, Parameter>::iterator pariter;
1793 pariter = m_funcParameters.find(parname);
1794 if (pariter == m_funcParameters.end()) {
1795 stringstream errss;
1796 errss << "Parameter " << parname << " does not exisit Le Bail function parameters. ";
1797 g_log.error(errss.str());
1798 throw runtime_error(errss.str());
1799 }
1800
1801 if (pariter->second.fit)
1802 parnamesforMC.emplace_back(parname);
1803}
1804
1805//----------------------------------------------------------------------------------------------
1825bool LeBailFit::calculateDiffractionPattern(const HistogramX &vecX, const HistogramY &vecY, bool inputraw,
1826 bool outputwithbkgd, const HistogramY &vecBkgd, std::vector<double> &values,
1827 Rfactor &rfactor) {
1828 HistogramY veccalbkgd(vecX.size());
1829 bool veccalbkgdIsEmpty = true;
1830
1831 // Examine whether all peaks are valid
1832 double maxfwhm = vecX.back() - vecX.front();
1833 bool peaksvalid = m_lebailFunction->isParameterValid(maxfwhm);
1834
1835 // If not valid, then return error message
1836 if (!peaksvalid) {
1837 g_log.information() << "Proposed new instrument profile values cause peak(s) to have "
1838 << "unphysical parameter values.\n";
1839 rfactor = badR;
1840 return false;
1841 }
1842
1843 // Calculate peaks' height
1844 if (inputraw) {
1845 // Remove background
1846 vector<double> vecPureY(vecY.size(), 0.);
1847 if (vecBkgd.size() == vecY.size()) {
1848 // Use input background
1849 g_log.information() << "Calculate diffraction pattern from raw and input "
1850 "background vector. "
1851 << ".\n";
1852 ::transform(vecY.begin(), vecY.end(), vecBkgd.begin(), vecPureY.begin(), ::minus<double>());
1853 } else {
1854 // Calculate background
1855 g_log.information() << "Calculate diffraction pattern from input data "
1856 "and newly calculated background. "
1857 << ".\n";
1858 veccalbkgd = m_lebailFunction->function(vecX.rawData(), false, true);
1859 ::transform(vecY.begin(), vecY.end(), veccalbkgd.begin(), vecPureY.begin(), ::minus<double>());
1860 veccalbkgdIsEmpty = false;
1861 }
1862
1863 // Calculate peak intensity
1864 peaksvalid = m_lebailFunction->calculatePeaksIntensities(vecX.rawData(), vecPureY, values);
1865 } // [input is raw]
1866 else {
1867 // Calculate peaks intensities
1868 g_log.debug() << "Calculate diffraction pattern from input data with "
1869 "background removed. "
1870 << ".\n";
1871 peaksvalid = m_lebailFunction->calculatePeaksIntensities(vecX.rawData(), vecY.rawData(), values);
1872 }
1873
1874 // Calculate Le Bail function
1875 if (values.size() != vecY.size()) {
1876 g_log.error() << "Input/output vector 'values' has a wrong size = " << values.size() << ". Resize it to "
1877 << vecY.size() << ".\n";
1878 throw runtime_error("Impossible...");
1879 }
1880
1881 // Integrated with background if required
1882 if (outputwithbkgd) {
1883 if (vecBkgd.size() == vecY.size()) {
1884 ::transform(values.begin(), values.end(), vecBkgd.begin(), values.begin(), ::plus<double>());
1885 } else {
1886 if (veccalbkgdIsEmpty)
1887 throw runtime_error("Programming logic error.");
1888 ::transform(values.begin(), values.end(), veccalbkgd.begin(), values.begin(), ::plus<double>());
1889 }
1890 rfactor = getRFactor(m_dataWS->y(m_wsIndex).rawData(), values, m_dataWS->e(m_wsIndex).rawData());
1891 } else {
1892 vector<double> caldata(values.size(), 0.0);
1893 if (vecBkgd.size() == vecY.size()) {
1894 // Use input background vector
1895 std::transform(values.begin(), values.end(), vecBkgd.begin(), caldata.begin(), std::plus<double>());
1896 } else {
1897 // Re-calculate background
1898 if (veccalbkgdIsEmpty)
1899 throw runtime_error("Programming logic error (2). ");
1900 std::transform(values.begin(), values.end(), veccalbkgd.begin(), caldata.begin(), std::plus<double>());
1901 }
1902 rfactor = getRFactor(m_dataWS->y(m_wsIndex).rawData(), caldata, m_dataWS->e(m_wsIndex).rawData());
1903 }
1904
1905 if (!peaksvalid) {
1906 g_log.information("LeBailFit: Some peaks have unphysical peak value. ");
1907 rfactor = badR;
1908 return false;
1909 }
1910
1911 return true;
1912}
1913
1914//----------------------------------------------------------------------------------------------
1928bool LeBailFit::proposeNewValues(const vector<string> &mcgroup, Rfactor r, map<string, Parameter> &curparammap,
1929 map<string, Parameter> &newparammap, bool prevBetterRwp) {
1930 // TODO: Study the possibility to merge curparammap and newparammap
1931
1932 // Initialize some flags
1933 bool anyparamtorefine = false;
1934
1935 // Find out parameters to refine in this step/MC group
1936 g_log.debug() << "Parameter Number In Group = " << mcgroup.size() << "\n";
1937 for (const auto &paramname : mcgroup) {
1938 // Find out the i-th parameter to be refined or not
1939 auto mapiter = curparammap.find(paramname);
1940 if (mapiter == curparammap.end()) {
1941 stringstream errmsg;
1942 errmsg << "Parameter to update (" << paramname << ") is not in the pool of parameters to get updated. "
1943 << ".\n"
1944 << "Number of parameters to update in this group = " << curparammap.size() << ". They are ";
1945 for (mapiter = curparammap.begin(); mapiter != curparammap.end(); ++mapiter) {
1946 errmsg << mapiter->first << ", ";
1947 }
1948 g_log.error(errmsg.str());
1949 throw runtime_error(errmsg.str());
1950 }
1951 Parameter &param = mapiter->second;
1952
1953 if (param.fit)
1954 anyparamtorefine = true;
1955 else
1956 continue;
1957
1958 // Pick a random number between -1 and 1 and calculate step size
1959 double randomnumber = 2 * static_cast<double>(rand()) / static_cast<double>(RAND_MAX) - 1.0;
1960
1961 // FIXME - [RPRWP] Try using Rp this time.
1962 double weight = r.Rwp;
1963 if (weight > 1.0)
1964 weight = 1.0;
1965 double stepsize = m_dampingFactor * weight * (param.curvalue * param.mcA1 + param.mcA0) * randomnumber;
1966 if (fabs(stepsize) > 0.5 * (param.maxvalue - param.minvalue))
1967 stepsize = fabs(stepsize) / stepsize * 0.5 * (param.maxvalue - param.minvalue);
1968
1969 // Direction of new value: drunk walk or random walk
1970 double newvalue;
1971 if (m_walkStyle == RANDOMWALK) {
1972 // Random walk. No preference on direction
1973 newvalue = param.curvalue + stepsize;
1974 } else if (m_walkStyle == DRUNKENWALK) {
1975 // Drunken walk. Prefer to previous successful move direction
1976 int prevRightDirection;
1977 if (prevBetterRwp)
1978 prevRightDirection = 1;
1979 else
1980 prevRightDirection = -1;
1981
1982 double randirint = static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
1983 g_log.debug() << "[TestRandom] random = " << randirint << "\n";
1984
1985 // FIXME Here are some MAGIC numbers
1986 if (randirint < 0.1) {
1987 // Negative direction to previous direction
1988 stepsize = -1.0 * fabs(stepsize) * static_cast<double>(param.movedirection * prevRightDirection);
1989 } else if (randirint < 0.4) {
1990 // No preferance and thus do nothing
1991 } else {
1992 // Positive direction to previous direction
1993 stepsize = fabs(stepsize) * static_cast<double>(param.movedirection * prevRightDirection);
1994 }
1995
1996 newvalue = param.curvalue + stepsize;
1997 } else {
1998 throw runtime_error("Unrecoganized walk style. ");
1999 }
2000
2001 // Restriction on the new value: non-negative
2002 if (param.nonnegative && newvalue < 0) {
2003 // If not allowed to be negative
2004 newvalue = fabs(newvalue);
2005 }
2006
2007 // Restriction on the new value: keep the new value in the boundary
2008 if (newvalue < param.minvalue) {
2009 int toss = rand() % 2;
2010 double direction = -1.0;
2011 newvalue = limitProposedValueInBound(param, newvalue, direction, toss);
2012 } else if (newvalue > param.maxvalue) {
2013 int toss = rand() % 2;
2014 double direction = 1.0;
2015 newvalue = limitProposedValueInBound(param, newvalue, direction, toss);
2016 }
2017
2018 // Apply to new parameter map
2019 auto newmiter = newparammap.find(paramname);
2020 if (newmiter == newparammap.end())
2021 throw runtime_error("New parameter map does not contain parameter that is updated.");
2022 newmiter->second.curvalue = newvalue;
2023 g_log.debug() << "[ProposeNewValue] " << paramname << " --> " << newvalue << "; random number = " << randomnumber
2024 << "\n";
2025
2026 // g) record some trace
2027 Parameter &p = param;
2028 if (stepsize > 0) {
2029 p.movedirection = 1;
2030 ++p.numpositivemove;
2031 } else if (stepsize < 0) {
2032 p.movedirection = -1;
2033 ++p.numnegativemove;
2034 } else {
2035 p.movedirection = -1;
2036 ++p.numnomove;
2037 }
2038 p.sumstepsize += fabs(stepsize);
2039 if (fabs(stepsize) > p.maxabsstepsize)
2040 p.maxabsstepsize = fabs(stepsize);
2041
2042 if (newvalue > p.maxrecordvalue)
2043 p.maxrecordvalue = newvalue;
2044 else if (newvalue < p.minrecordvalue)
2045 p.minrecordvalue = newvalue;
2046
2047 g_log.debug() << "[DBx257] " << paramname << "\t"
2048 << "Proposed value = " << setw(15) << newvalue << " (orig = " << param.curvalue
2049 << ", step = " << stepsize << "), totRwp = " << r.Rwp << "\n";
2050 } // ENDFOR (i): Each parameter in this MC group/step
2051
2052 return anyparamtorefine;
2053}
2054
2055//-----------------------------------------------------------------------------------------------
2067double LeBailFit::limitProposedValueInBound(const Parameter &param, double newvalue, double direction, int choice) {
2068 if (choice == 0) {
2069 // Half distance
2070 if (direction > 0) {
2071 newvalue = (param.maxvalue - param.curvalue) * 0.5 + param.curvalue;
2072 } else {
2073 newvalue = param.minvalue + 0.5 * (param.curvalue - param.minvalue);
2074 }
2075 } else {
2076 double deltaX = param.maxvalue - param.minvalue;
2077
2078 if (deltaX < NOBOUNDARYLIMIT) {
2079 choice = 1; // periodic
2080 } else {
2081 choice = 2; // reflection
2082 }
2083
2084 if (choice == 1) {
2085 // Periodic boundary
2086 if (direction > 0) {
2087 // newvalue = param.minvalue + (newvalue - param.maxvalue) % deltaX;
2088 double dval = (newvalue - param.maxvalue) / deltaX;
2089 newvalue = param.minvalue + deltaX * (dval - floor(dval));
2090 } else {
2091 // newvalue = param.maxvalue - (param.minvalue - newvalue) % deltaX;
2092 double dval = (param.minvalue - newvalue) / deltaX;
2093 newvalue = param.maxvalue - deltaX * (dval - floor(dval));
2094 }
2095 } else {
2096 // Reflective boundary
2097 if (direction > 0) {
2098 newvalue = param.maxvalue - (newvalue - param.maxvalue);
2099 } else {
2100 newvalue = param.minvalue + (param.maxvalue - newvalue);
2101 }
2102 }
2103 }
2104
2105 return newvalue;
2106}
2107
2108//-----------------------------------------------------------------------------------------------
2115 bool accept;
2116
2117 // FIXME - [RPRWP] Using Rp for peak fitting
2118 double new_goodness = newR.Rwp;
2119 double cur_goodness = currR.Rwp;
2120
2121 if (new_goodness < cur_goodness) {
2122 // Lower Rwp. Take the change
2123 accept = true;
2124 } else if (new_goodness > 1.0 - 1.0E-9) {
2125 // Too high
2126 g_log.debug() << "Goodness > " << 1.0 - 1.0E-9 << ". Reject!"
2127 << ".\n";
2128 accept = false;
2129 } else {
2130 // Higher Rwp/Rp. Take a chance to accept
2131 double dice = static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
2132 g_log.debug() << "[TestRandom] dice " << dice << "\n";
2133 double bar = exp(-(new_goodness - cur_goodness) / (cur_goodness * m_Temperature));
2134
2135 accept = dice < bar;
2136 }
2137
2138 return accept;
2139}
2140
2141//----------------------------------------------------------------------------------------------
2150void LeBailFit::bookKeepBestMCResult(const map<string, Parameter> &parammap, const vector<double> &bkgddata,
2151 Rfactor rfactor, size_t istep) {
2152 // TODO : [RPRWP] Here is a metric of goodness of it.
2153 double goodness = rfactor.Rwp;
2154 bool better = goodness < m_bestRwp;
2155
2156 if (better) {
2157 // In case obtain the best solution so far
2158
2159 // a) Record goodness and step
2160 m_bestRwp = rfactor.Rwp;
2161 m_bestRp = rfactor.Rp;
2162 m_bestMCStep = istep;
2163
2164 // b) Record parameters
2165 if (m_bestParameters.empty()) {
2166 // If not be initialized, initialize it!
2167 m_bestParameters = parammap;
2168 } else {
2169 // in case initialized, copy the value over
2171 }
2172
2173 // c) Background
2174 m_bestBackgroundData = bkgddata;
2175 } else {
2176 // In code calling this function, it should be better always.
2177 g_log.warning("[Book keep best MC result] Shouldn't be here as it is found "
2178 "that it is not the best solution ");
2179 }
2180}
2181
2182//------------------------------------------------------------------------------------------------
2188void LeBailFit::applyParameterValues(const map<string, Parameter> &srcparammap, map<string, Parameter> &tgtparammap) {
2189 map<string, Parameter>::const_iterator srcmapiter;
2190 map<string, Parameter>::iterator tgtmapiter;
2191 for (srcmapiter = srcparammap.begin(); srcmapiter != srcparammap.end(); ++srcmapiter) {
2192 string parname = srcmapiter->first;
2193 Parameter srcparam = srcmapiter->second;
2194
2195 tgtmapiter = tgtparammap.find(parname);
2196 if (tgtmapiter == tgtparammap.end()) {
2197 stringstream errss;
2198 errss << "Parameter " << parname << " cannot be found in target Parameter map containing " << tgtparammap.size()
2199 << " entries. ";
2200 g_log.error(errss.str());
2201 throw runtime_error("Programming or memory error! This situation cannot happen!");
2202 }
2203
2204 tgtmapiter->second.curvalue = srcparam.curvalue;
2205 }
2206}
2207
2209std::map<std::string, double> LeBailFit::convertToDoubleMap(std::map<std::string, Parameter> &inmap) {
2210 std::map<std::string, double> outmap;
2211 std::map<std::string, Parameter>::iterator miter;
2212 for (miter = inmap.begin(); miter != inmap.end(); ++miter) {
2213 outmap.emplace(miter->first, miter->second.curvalue);
2214 }
2215
2216 return outmap;
2217}
2218
2219// ============================ External Auxiliary Functions
2220// =================================
2221
2224void writeRfactorsToFile(const vector<double> &vecX, const vector<Rfactor> &vecR, const string &filename) {
2225 ofstream ofile;
2226 ofile.open(filename.c_str());
2227
2228 for (size_t i = 0; i < vecX.size(); ++i)
2229 ofile << setw(15) << setprecision(5) << vecX[i] << setw(15) << setprecision(5) << vecR[i].Rwp << setw(15)
2230 << setprecision(5) << vecR[i].Rp << "\n";
2231
2232 ofile.close();
2233}
2234
2235} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double height
Definition GetAllEi.cpp:155
const double EPSILON(1.0E-10)
const int DATADIFFINDEX(2)
const int CALDATAINDEX(1)
const int INPUTPUREPEAKINDEX(7)
const int OBSDATAINDEX(0)
const int CALBKGDINDEX(4)
const double NOBOUNDARYLIMIT(1.0E10)
const int CALPUREPEAKINDEX(3)
const int INPUTBKGDINDEX(6)
#define fabs(x)
Definition Matrix.cpp:22
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:422
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
Implements FunctionDomain1D with its own storage in form of a std::vector.
A class to store values calculated by a function.
const std::vector< double > & toVector() const
Return the calculated values as a vector.
TableRowHelper appendRow()
Appends a row.
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
size_t size() const
Returns the number of rows in the TableWorkspace.
Definition TableRow.h:45
A property class for workspaces.
bool calculateDiffractionPattern(const Mantid::HistogramData::HistogramX &vecX, const Mantid::HistogramData::HistogramY &vecY, bool inputraw, bool outputwithbkgd, const Mantid::HistogramData::HistogramY &vecBkgd, std::vector< double > &values, Kernel::Rfactor &rfactor)
Calculate diffraction pattern in Le Bail algorithm for MC Random walk.
void proposeNewBackgroundValues()
Propose new background parameters.
bool acceptOrDeny(Kernel::Rfactor currR, Kernel::Rfactor newR)
Determine whether the proposed value should be accepted or denied.
std::vector< std::pair< std::vector< int >, double > > m_inputPeakInfoVec
Input Bragg peak information for future processing;.
Definition LeBailFit.h:200
Functions::BackgroundFunction_sptr m_backgroundFunction
Background function.
Definition LeBailFit.h:203
bool m_tolerateInputDupHKL2Peaks
Flag to allow peaks with duplicated (HKL)^2 in input .hkl file.
Definition LeBailFit.h:274
std::map< std::string, double > m_origFuncParameters
Input function parameters that are stored for reference.
Definition LeBailFit.h:208
void addParameterToMCMinimize(std::vector< std::string > &parnamesforMC, const std::string &parname)
Add parameter (to a vector of string/name) for MC random walk.
bool m_inputParameterPhysical
Flag to show whether the input profile parameters are physical to all peaks.
Definition LeBailFit.h:239
void exec() override
Implement abstract Algorithm methods.
API::MatrixWorkspace_sptr cropWorkspace(const API::MatrixWorkspace_sptr &inpws, size_t wsindex)
Crop the workspace for better usage.
void doMarkovChain(const std::map< std::string, Parameter > &parammap, const Mantid::HistogramData::HistogramX &vecX, const Mantid::HistogramData::HistogramY &vecPurePeak, const std::vector< double > &vecBkgd, size_t maxcycles, const Kernel::Rfactor &startR, int randomseed)
Work on Markov chain to 'solve' LeBail function.
void init() override
Declare the input properties for this algorithm.
Definition LeBailFit.cpp:74
std::map< std::string, Parameter > m_funcParameters
Function parameters updated by fit.
Definition LeBailFit.h:206
LeBailFunction_sptr m_lebailFunction
Le Bail Function (Composite)
Definition LeBailFit.h:187
std::vector< double > m_backgroundParameters
Background polynomials.
Definition LeBailFit.h:221
std::vector< double > m_bkgdParameterBuffer
Definition LeBailFit.h:280
void parseInstrumentParametersTable()
Import peak parameters.
std::vector< std::string > m_bkgdParameterNames
Definition LeBailFit.h:278
void exportInstrumentParameterToTable(const std::map< std::string, Parameter > &parammap)
Output parameters (fitted or tied)
void processInputBackground()
Process and calculate input background.
void parseBraggPeaksParametersTable()
Import Miller Indices (HKL)
double limitProposedValueInBound(const Parameter &param, double newvalue, double direction, int choice)
Limit proposed value in the specified boundary.
void applyParameterValues(const std::map< std::string, Parameter > &srcparammap, std::map< std::string, Parameter > &tgtparammap)
Apply the value of parameters in the source to target.
std::map< std::string, double > convertToDoubleMap(std::map< std::string, Parameter > &inmap)
Convert a map of Parameter to a map of double.
void execRefineBackground()
Calcualte background by fitting peak heights.
double m_minimumPeakHeight
Minimum height of a peak to be counted in smoothing background.
Definition LeBailFit.h:271
DataObjects::Workspace2D_sptr m_outputWS
Definition LeBailFit.h:191
void execPatternCalculation()
Calculate LeBail pattern from from input peak parameters.
void recoverBackgroundParameters(const std::vector< double > &bkgdparamvec)
Restore/recover the buffered background parameters to m_background function.
std::map< int, std::vector< std::string > > m_MCGroups
Definition LeBailFit.h:247
bool m_useAnnealing
Flag to use Annealing Simulation (i.e., use automatic adjusted temperature)
Definition LeBailFit.h:265
API::MatrixWorkspace_sptr m_dataWS
Instance data.
Definition LeBailFit.h:190
size_t m_numMinimizeSteps
Number of minimization steps. For both MC and regular.
Definition LeBailFit.h:258
void setupBuiltInRandomWalkStrategy()
Set up Monte Carlo random walk strategy.
void storeBackgroundParameters(std::vector< double > &bkgdparamvec)
Store/buffer current background parameters.
void exportBraggPeakParameterToTable()
Create and set up output table workspace for peaks.
std::map< std::string, Parameter > m_bestParameters
Definition LeBailFit.h:253
enum Mantid::CurveFitting::Algorithms::LeBailFit::@2 m_walkStyle
Monte Carlo algorithm.
void createOutputDataWorkspace()
Create output data workspace.
DataObjects::TableWorkspace_sptr parameterWS
Definition LeBailFit.h:192
double m_Temperature
Monte Carlo temperature.
Definition LeBailFit.h:261
void parseBackgroundTableWorkspace(const DataObjects::TableWorkspace_sptr &bkgdparamws, std::vector< std::string > &bkgdparnames, std::vector< double > &bkgdorderparams)
Parse content in a table workspace to vector for background parameters.
void bookKeepBestMCResult(const std::map< std::string, Parameter > &parammap, const std::vector< double > &bkgddata, Kernel::Rfactor rfactor, size_t istep)
Book keep the (sopposed) best MC result.
std::vector< std::string > m_backgroundParameterNames
Definition LeBailFit.h:222
std::string m_backgroundType
Background type.
Definition LeBailFit.h:218
void processInputProperties()
Process input properties.
void execRandomWalkMinimizer(size_t maxcycles, std::map< std::string, Parameter > &parammap)
Main for random walk process.
std::string m_peakType
============================= =========================== ///
Definition LeBailFit.h:215
std::vector< double > m_bkgdParameterStepVec
Definition LeBailFit.h:283
void createLeBailFunction()
Create LeBailFunction.
DataObjects::TableWorkspace_sptr reflectionWS
Definition LeBailFit.h:193
bool proposeNewValues(const std::vector< std::string > &mcgroup, Kernel::Rfactor r, std::map< std::string, Parameter > &curparammap, std::map< std::string, Parameter > &newparammap, bool prevBetterRwp)
Propose new parameters.
void setupRandomWalkStrategyFromTable(const DataObjects::TableWorkspace_sptr &tablews)
Set up Monte Carlo random walk strategy.
LeBailFunction : LeBailFunction is to calculate peak intensities in a composite function including ne...
TableWorkspace is an implementation of Workspace in which the data are organised in columns of same s...
API::Column_sptr addColumn(const std::string &type, const std::string &name) override
Creates a new column.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
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
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< IPowderDiffPeakFunction > IPowderDiffPeakFunction_sptr
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void writeRfactorsToFile(const std::vector< double > &vecX, const std::vector< Kernel::Rfactor > &vecR, const std::string &filename)
Write a set of (XY) data to a column file.
const Rfactor badR(DBL_MAX, DBL_MAX)
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
std::shared_ptr< IValidator > IValidator_sptr
A shared_ptr to an IValidator.
Definition IValidator.h:26
Rfactor MANTID_KERNEL_DLL getRFactor(const std::vector< double > &obsI, const std::vector< double > &calI, const std::vector< double > &obsE)
Return the R-factors (Rwp) of a diffraction pattern data.
Helper class which provides the Collimation Length for SANS instruments.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
STL namespace.
LeBailFit : Algorithm to do Le Bail Fit.
Definition LeBailFit.h:37
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54
R factor for powder data analysis.
Definition Statistics.h:65