Mantid
Loading...
Searching...
No Matches
IMWDomainCreator.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 +
7// Includes
8//----------------------------------------------------------------------
14
24
25#include "MantidAPI/TextAxis.h"
28#include "MantidKernel/Matrix.h"
29
30#include <algorithm>
31#include <boost/math/distributions/students_t.hpp>
32
33namespace Mantid::CurveFitting {
34
35namespace {
36bool greaterIsLess(double x1, double x2) { return x1 > x2; }
37} // namespace
38
39using namespace Kernel;
40using API::MatrixWorkspace;
41using API::Workspace;
42
50IMWDomainCreator::IMWDomainCreator(Kernel::IPropertyManager *fit, const std::string &workspacePropertyName,
52 : API::IDomainCreator(fit, std::vector<std::string>(1, workspacePropertyName), domainType), m_workspaceIndex(-1),
53 m_startX(EMPTY_DBL()), m_endX(EMPTY_DBL()), m_startIndex(0) {
54 if (m_workspacePropertyNames.empty()) {
55 throw std::runtime_error("Cannot create FitMW: no workspace given");
56 }
58}
59
64 // if property manager is set overwrite any set parameters
65 if (m_manager) {
66
67 // get the workspace
69 m_matrixWorkspace = std::dynamic_pointer_cast<API::MatrixWorkspace>(ws);
70 if (!m_matrixWorkspace) {
71 throw std::invalid_argument("InputWorkspace must be a MatrixWorkspace.");
72 }
73
75 m_workspaceIndex = static_cast<size_t>(index);
78 }
79}
80
87void IMWDomainCreator::declareDatasetProperties(const std::string &suffix, bool addProp) {
88 m_workspaceIndexPropertyName = "WorkspaceIndex" + suffix;
89 m_startXPropertyName = "StartX" + suffix;
90 m_endXPropertyName = "EndX" + suffix;
91
93 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
94 mustBePositive->setLower(0);
96 "The Workspace Index to fit in the input workspace");
98 "A value of x in, or on the low x boundary of, the first bin to "
99 "include in\n"
100 "the fit (default lowest value of x)");
102 "A value in, or on the high x boundary of, the last bin the fitting "
103 "range\n"
104 "(default the highest value of x)");
105 }
106}
107
112std::pair<size_t, size_t> IMWDomainCreator::getXInterval() const {
113
114 const auto &X = m_matrixWorkspace->x(m_workspaceIndex);
115 if (X.empty()) {
116 throw std::runtime_error("Workspace contains no data.");
117 }
118
120
121 // From points to the first occurrence of StartX in the workspace interval.
122 // End points to the last occurrence of EndX in the workspace interval.
123 // Find the fitting interval: from -> to
124 Mantid::MantidVec::const_iterator from;
125 Mantid::MantidVec::const_iterator to;
126
127 bool isXAscending = X.front() < X.back();
128
129 if (m_startX == EMPTY_DBL() && m_endX == EMPTY_DBL()) {
130 m_startX = X.front();
131 from = X.begin();
132 m_endX = X.back();
133 to = X.end();
134 } else if (m_startX == EMPTY_DBL() || m_endX == EMPTY_DBL()) {
135 throw std::invalid_argument("Both StartX and EndX must be given to set fitting interval.");
136 } else if (isXAscending) {
137 if (m_startX > m_endX) {
138 std::swap(m_startX, m_endX);
139 }
140 from = std::lower_bound(X.begin(), X.end(), m_startX);
141 to = std::upper_bound(from, X.end(), m_endX);
142 } else { // x is descending
143 if (m_startX < m_endX) {
144 std::swap(m_startX, m_endX);
145 }
146 from = std::lower_bound(X.begin(), X.end(), m_startX, greaterIsLess);
147 to = std::upper_bound(from, X.end(), m_endX, greaterIsLess);
148 }
149
150 // Check whether the fitting interval defined by StartX and EndX is 0.
151 // This occurs when StartX and EndX are both less than the minimum workspace
152 // x-value or greater than the maximum workspace x-value.
153 if (to - from == 0) {
154 throw std::invalid_argument("StartX and EndX values do not capture a range "
155 "within the workspace interval.");
156 }
157
158 if (m_matrixWorkspace->isHistogramData()) {
159 if (X.end() == to) {
160 to = X.end() - 1;
161 }
162 }
163 return std::make_pair(std::distance(X.begin(), from), std::distance(X.begin(), to));
164}
165
171 size_t startIndex, endIndex;
172 std::tie(startIndex, endIndex) = getXInterval();
173 return endIndex - startIndex;
174}
175
182 if (!function) {
183 throw std::runtime_error("Cannot initialize empty function.");
184 }
185 function->setWorkspace(m_matrixWorkspace);
186 function->setMatrixWorkspace(m_matrixWorkspace, m_workspaceIndex, m_startX, m_endX);
187 setInitialValues(*function);
188}
189
197API::MatrixWorkspace_sptr IMWDomainCreator::createEmptyResultWS(const size_t nhistograms, const size_t nyvalues) {
198 size_t nxvalues(nyvalues);
199 if (m_matrixWorkspace->isHistogramData())
200 nxvalues += 1;
202 API::WorkspaceFactory::Instance().create("Workspace2D", nhistograms, nxvalues, nyvalues);
203 ws->setTitle("");
204 ws->setYUnitLabel(m_matrixWorkspace->YUnitLabel());
205 ws->setYUnit(m_matrixWorkspace->YUnit());
206 ws->getAxis(0)->unit() = m_matrixWorkspace->getAxis(0)->unit();
207 auto tAxis = std::make_unique<API::TextAxis>(nhistograms);
208 ws->replaceAxis(1, std::move(tAxis));
209
210 auto &inputX = m_matrixWorkspace->x(m_workspaceIndex);
211 auto &inputY = m_matrixWorkspace->y(m_workspaceIndex);
212 auto &inputE = m_matrixWorkspace->e(m_workspaceIndex);
213 // X values for all
214 for (size_t i = 0; i < nhistograms; i++) {
215 ws->mutableX(i).assign(inputX.begin() + m_startIndex, inputX.begin() + m_startIndex + nxvalues);
216 }
217 // Data values for the first histogram
218 ws->mutableY(0).assign(inputY.begin() + m_startIndex, inputY.begin() + m_startIndex + nyvalues);
219 ws->mutableE(0).assign(inputE.begin() + m_startIndex, inputE.begin() + m_startIndex + nyvalues);
220
221 return ws;
222}
223
229 auto domain = m_domain.lock();
230 auto values = m_values.lock();
231 if (domain && values) {
232 ParameterEstimator::estimate(function, *domain, *values);
233 }
234}
235
244std::shared_ptr<API::Workspace> IMWDomainCreator::createOutputWorkspace(
245 const std::string &baseName, API::IFunction_sptr function, std::shared_ptr<API::FunctionDomain> domain,
246 std::shared_ptr<API::FunctionValues> values, const std::string &outputWorkspacePropertyName) {
247 if (!values) {
248 throw std::logic_error("FunctionValues expected");
249 }
250
251 // Compile list of functions to output. The top-level one is first
252 std::list<API::IFunction_sptr> functionsToDisplay(1, function);
254 appendCompositeFunctionMembers(functionsToDisplay, function);
255 }
256
257 // Nhist = Data histogram, Difference Histogram + nfunctions
258 const size_t nhistograms = functionsToDisplay.size() + 2;
259 const size_t nyvalues = values->size();
260 auto ws = createEmptyResultWS(nhistograms, nyvalues);
261 // The workspace was constructed with a TextAxis
262 API::TextAxis *textAxis = static_cast<API::TextAxis *>(ws->getAxis(1));
263 textAxis->setLabel(0, "Data");
264 textAxis->setLabel(1, "Calc");
265 textAxis->setLabel(2, "Diff");
266
267 // Add each calculated function
268 auto iend = functionsToDisplay.end();
269 size_t wsIndex(1); // Zero reserved for data
270 for (auto it = functionsToDisplay.begin(); it != iend; ++it) {
271 if (wsIndex > 2)
272 textAxis->setLabel(wsIndex, (*it)->name());
273 addFunctionValuesToWS(*it, ws, wsIndex, domain, values);
274 if (it == functionsToDisplay.begin())
275 wsIndex += 2; // Skip difference histogram for now
276 else
277 ++wsIndex;
278 }
279
280 // Set the difference spectrum
281 const auto &Ycal = ws->y(1);
282 auto &Diff = ws->mutableY(2);
283 const size_t nData = values->size();
284 for (size_t i = 0; i < nData; ++i) {
285 if (values->getFitWeight(i) != 0.0) {
286 Diff[i] = values->getFitData(i) - Ycal[i];
287 } else {
288 Diff[i] = 0.0;
289 }
290 }
291
292 if (!outputWorkspacePropertyName.empty()) {
294 "Name of the output Workspace holding resulting simulated spectrum");
295 m_manager->setPropertyValue(outputWorkspacePropertyName, baseName + "Workspace");
296 m_manager->setProperty(outputWorkspacePropertyName, ws);
297 }
298
299 // If the input is a not an EventWorkspace and is a distrubution, then convert
300 // the output also to a distribution
301 if (!std::dynamic_pointer_cast<Mantid::API::IEventWorkspace>(m_matrixWorkspace)) {
302 if (m_matrixWorkspace->isDistribution()) {
303 ws->setDistribution(true);
304 }
305 }
306
307 return ws;
308}
309
314void IMWDomainCreator::appendCompositeFunctionMembers(std::list<API::IFunction_sptr> &functionList,
315 const API::IFunction_sptr &function) const {
316 // if function is a Convolution then output of convolved model's mebers may be
317 // required
318 if (m_convolutionCompositeMembers && std::dynamic_pointer_cast<Functions::Convolution>(function)) {
319 appendConvolvedCompositeFunctionMembers(functionList, function);
320 } else {
321 const auto compositeFn = std::dynamic_pointer_cast<API::CompositeFunction>(function);
322 if (!compositeFn)
323 return;
324
325 const size_t nlocals = compositeFn->nFunctions();
326 for (size_t i = 0; i < nlocals; ++i) {
327 auto localFunction = compositeFn->getFunction(i);
328 auto localComposite = std::dynamic_pointer_cast<API::CompositeFunction>(localFunction);
329 if (localComposite)
330 appendCompositeFunctionMembers(functionList, localComposite);
331 else
332 functionList.insert(functionList.end(), localFunction);
333 }
334 }
335}
336
350void IMWDomainCreator::appendConvolvedCompositeFunctionMembers(std::list<API::IFunction_sptr> &functionList,
351 const API::IFunction_sptr &function) const {
352 std::shared_ptr<Functions::Convolution> convolution = std::dynamic_pointer_cast<Functions::Convolution>(function);
353
354 const auto compositeFn = std::dynamic_pointer_cast<API::CompositeFunction>(convolution->getFunction(1));
355 if (!compositeFn) {
356 functionList.insert(functionList.end(), convolution);
357 } else {
358 auto resolution = convolution->getFunction(0);
359 const size_t nlocals = compositeFn->nFunctions();
360 for (size_t i = 0; i < nlocals; ++i) {
361 auto localFunction = compositeFn->getFunction(i);
362 std::shared_ptr<Functions::Convolution> localConvolution = std::make_shared<Functions::Convolution>();
363 localConvolution->addFunction(resolution);
364 localConvolution->addFunction(localFunction);
365 functionList.insert(functionList.end(), localConvolution);
366 }
367 }
368}
369
380 std::shared_ptr<API::MatrixWorkspace> &ws, const size_t wsIndex,
381 const std::shared_ptr<API::FunctionDomain> &domain,
382 const std::shared_ptr<API::FunctionValues> &resultValues) const {
383 const size_t nData = resultValues->size();
384 resultValues->zeroCalculated();
385 // Confidence bands are calculated based on the example in
386 // www.astro.rug.nl/software/kapteyn/kmpfittutorial.html#confidence-and-prediction-intervals,
387 // which references J.Wolberg, Data Analysis Using the Method of Least Squares, 2006, Springer.
388 // Here we assume a confidence band of 1 sigma
389 double sigma = 1;
390 double prob = std::erf(sigma / sqrt(2));
391 // critical value for t distribution
392 double alpha = (1 + prob) / 2;
393
394 // Function value
395 function->function(*domain, *resultValues);
396
397 size_t nParams = function->nParams();
398
399 // the function should contain the parameter's covariance matrix
400 auto covar = function->getCovarianceMatrix();
401 bool hasErrors = false;
402 if (!covar) {
403 for (size_t j = 0; j < nParams; ++j) {
404 if (function->getError(j) != 0.0) {
405 hasErrors = true;
406 break;
407 }
408 }
409 }
410
411 if (covar || hasErrors) {
412 // and errors
413 Jacobian J(nData, nParams);
414 try {
415 function->functionDeriv(*domain, J);
416 } catch (...) {
417 function->calNumericalDeriv(*domain, J);
418 }
419 if (covar) {
420 // if the function has a covariance matrix attached - use it for the
421 // errors
422 const Kernel::Matrix<double> &C = *covar;
423 // The formula is E = J * C * J^T
424 // We don't do full 3-matrix multiplication because we only need the
425 // diagonals of E
426 std::vector<double> E(nData);
427 for (size_t k = 0; k < nData; ++k) {
428 double s = 0.0;
429 for (size_t i = 0; i < nParams; ++i) {
430 double tmp = J.get(k, i);
431 s += C[i][i] * tmp * tmp;
432 for (size_t j = i + 1; j < nParams; ++j) {
433 s += J.get(k, i) * C[i][j] * J.get(k, j) * 2;
434 }
435 }
436 E[k] = s;
437 }
438
439 size_t dof = nData - nParams;
440 auto &yValues = ws->mutableY(wsIndex);
441 auto &eValues = ws->mutableE(wsIndex);
442 double T = 1.0;
443 if (dof != 0) {
444 boost::math::students_t dist(static_cast<double>(dof));
445 T = boost::math::quantile(dist, alpha);
446 }
447 for (size_t i = 0; i < nData; i++) {
448 yValues[i] = resultValues->getCalculated(i);
449 eValues[i] = T * std::sqrt(E[i]);
450 }
451 } else {
452 // otherwise use the parameter errors which is OK for uncorrelated
453 // parameters
454 auto &yValues = ws->mutableY(wsIndex);
455 auto &eValues = ws->mutableE(wsIndex);
456 for (size_t i = 0; i < nData; i++) {
457 yValues[i] = resultValues->getCalculated(i);
458 double err = 0.0;
459 for (size_t j = 0; j < nParams; ++j) {
460 double d = J.get(i, j) * function->getError(j);
461 err += d * d;
462 }
463 eValues[i] = std::sqrt(err);
464 }
465 }
466 } else {
467 // No errors
468 auto &yValues = ws->mutableY(wsIndex);
469 for (size_t i = 0; i < nData; i++) {
470 yValues[i] = resultValues->getCalculated(i);
471 }
472 }
473}
474
475} // namespace Mantid::CurveFitting
gsl_vector * tmp
double sigma
Definition: GetAllEi.cpp:156
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
An base class for domain creators for use in Fit.
DomainType
Type of domain to create.
bool m_convolutionCompositeMembers
Perform convolution of output composite components.
bool m_outputCompositeMembers
Output separate composite function values.
std::vector< std::string > m_workspacePropertyNames
Property names for workspaces to get the data from.
Kernel::IPropertyManager * m_manager
Pointer to a property manager.
void declareProperty(Kernel::Property *prop, const std::string &doc)
Declare a property to the algorithm.
This is an interface to a fitting function - a semi-abstarct class.
Definition: IFunction.h:163
Represents the Jacobian in IFitFunction::functionDeriv.
Definition: Jacobian.h:22
virtual double get(size_t iY, size_t iP)=0
Get the value to a Jacobian matrix element.
Class to represent a text axis of a workspace.
Definition: TextAxis.h:36
void setLabel(const std::size_t &index, const std::string &lbl)
Set the label at the given index.
Definition: TextAxis.cpp:104
A property class for workspaces.
void declareDatasetProperties(const std::string &suffix="", bool addProp=true) override
Declare properties that specify the dataset within the workspace to fit to.
std::string m_endXPropertyName
Store endX property name.
void appendCompositeFunctionMembers(std::list< API::IFunction_sptr > &functionList, const API::IFunction_sptr &function) const
std::string m_startXPropertyName
Store startX property name.
std::weak_ptr< API::FunctionDomain1D > m_domain
Store the created domain and values.
std::shared_ptr< API::MatrixWorkspace > createEmptyResultWS(const size_t nhistograms, const size_t nyvalues)
Creates the blank output workspace of the correct size.
std::pair< size_t, size_t > getXInterval() const
Calculate size and starting iterator in the X array.
std::shared_ptr< API::MatrixWorkspace > m_matrixWorkspace
The input MareixWorkspace.
std::weak_ptr< API::FunctionValues > m_values
IMWDomainCreator(Kernel::IPropertyManager *fit, const std::string &workspacePropertyName, DomainType domainType=Simple)
Constructor.
virtual void setParameters() const
Set all parameters.
void setInitialValues(API::IFunction &function)
Set initial values for parameters with default values.
void addFunctionValuesToWS(const API::IFunction_sptr &function, std::shared_ptr< API::MatrixWorkspace > &ws, const size_t wsIndex, const std::shared_ptr< API::FunctionDomain > &domain, const std::shared_ptr< API::FunctionValues > &resultValues) const
Add the calculated function values to the workspace.
size_t m_workspaceIndex
The workspace index.
std::string m_workspacePropertyName
Store workspace property name.
std::shared_ptr< API::Workspace > createOutputWorkspace(const std::string &baseName, API::IFunction_sptr function, std::shared_ptr< API::FunctionDomain > domain, std::shared_ptr< API::FunctionValues > values, const std::string &outputWorkspacePropertyName) override
Create an output workspace.
std::string m_workspaceIndexPropertyName
Store workspace index property name.
void appendConvolvedCompositeFunctionMembers(std::list< API::IFunction_sptr > &functionList, const API::IFunction_sptr &function) const
If the fit function is Convolution and flag m_convolutionCompositeMembers is set and Convolution's se...
void initFunction(API::IFunction_sptr function) override
Initialize the function.
size_t getDomainSize() const override
Return the size of the domain to be created.
Interface to PropertyManager.
virtual void setPropertyValue(const std::string &name, const std::string &value)=0
Sets property value from a string.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
virtual bool existsProperty(const std::string &name) const =0
Checks whether the named property is already in the list of managed property.
virtual TypedValue getProperty(const std::string &name) const =0
Get the value of a property.
Numerical Matrix class.
Definition: Matrix.h:42
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
Definition: IFunction.h:732
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void MANTID_CURVEFITTING_DLL estimate(API::IFunction &function, const API::FunctionDomain1D &domain, const API::FunctionValues &values)
ParameterEstimator estimates parameter values of some fitting functions from fitting data.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
STL namespace.
@ Output
An output workspace.
Definition: Property.h:54