Mantid
Loading...
Searching...
No Matches
IFittingAlgorithm.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
17
25
27
28namespace Mantid::CurveFitting {
29
30using namespace Mantid::Kernel;
31using namespace Mantid::API;
32
33//----------------------------------------------------------------------------------------------
34namespace {
36std::unique_ptr<IDomainCreator> createDomainCreator(const IFunction *fun, const std::string &workspacePropertyName,
37 IPropertyManager *manager, IDomainCreator::DomainType domainType) {
38
39 std::unique_ptr<IDomainCreator> creator;
41
42 try {
43 ws = manager->getProperty("InputWorkspace");
44 } catch (...) {
45 // InputWorkspace is not needed for some fit function so continue
46 }
47
48 // ILatticeFunction requires API::LatticeDomain.
49 if (dynamic_cast<const ILatticeFunction *>(fun)) {
50 creator = std::make_unique<LatticeDomainCreator>(manager, workspacePropertyName);
51 } else if (dynamic_cast<const IFunctionMD *>(fun)) {
52 creator = std::unique_ptr<IDomainCreator>(
53 API::DomainCreatorFactory::Instance().createDomainCreator("FitMD", manager, workspacePropertyName, domainType));
54 } else if (dynamic_cast<const IFunction1DSpectrum *>(fun)) {
55 creator = std::make_unique<SeqDomainSpectrumCreator>(manager, workspacePropertyName);
56 } else if (auto gfun = dynamic_cast<const IFunctionGeneral *>(fun)) {
57 creator = std::make_unique<GeneralDomainCreator>(*gfun, *manager, workspacePropertyName);
58 } else if (std::dynamic_pointer_cast<ITableWorkspace>(ws)) {
59 creator = std::make_unique<TableWorkspaceDomainCreator>(manager, workspacePropertyName, domainType);
60 } else {
61 bool histogramFit = manager->getPropertyValue("EvaluationType") == "Histogram";
62 if (histogramFit) {
63 creator = std::make_unique<HistogramDomainCreator>(*manager, workspacePropertyName);
64 } else {
65 creator = std::make_unique<FitMW>(manager, workspacePropertyName, domainType);
66 }
67 }
68 return creator;
69}
70} // namespace
71
72//----------------------------------------------------------------------------------------------
73
75const std::string IFittingAlgorithm::category() const { return "Optimization"; }
76
77//----------------------------------------------------------------------------------------------
81 declareProperty(std::make_unique<API::FunctionProperty>("Function", Direction::InOut),
82 "Parameters defining the fitting function and its initial values");
83
85 std::make_unique<API::WorkspaceProperty<API::Workspace>>("InputWorkspace", "", Kernel::Direction::Input),
86 "Name of the input Workspace");
87 declareProperty("IgnoreInvalidData", false, "Flag to ignore infinities, NaNs and data with zero errors.");
88
89 std::array<std::string, 3> domainTypes = {{"Simple", "Sequential", "Parallel"}};
90 declareProperty("DomainType", "Simple", Kernel::IValidator_sptr(new Kernel::ListValidator<std::string>(domainTypes)),
91 "The type of function domain to use: Simple, Sequential, or Parallel.", Kernel::Direction::Input);
92
93 std::array<std::string, 2> evaluationTypes = {{"CentrePoint", "Histogram"}};
94 declareProperty("EvaluationType", "CentrePoint",
96 "The way the function is evaluated on histogram data sets. "
97 "If value is \"CentrePoint\" then function is evaluated at "
98 "centre of each bin. If it is \"Histogram\" then function is "
99 "integrated within the bin and the integrals returned.",
101 const std::array<std::string, 2> stepSizes = {{"Default", "Sqrt epsilon"}};
103 "StepSizeMethod", "Default", Kernel::IValidator_sptr(new Kernel::ListValidator<std::string>(stepSizes)),
104 "The way the step size is calculated for numerical derivatives. See the section about step sizes in the Fit "
105 "algorithm documentation to understand the difference between \"Default\" and \"Sqrt epsilon\".",
107 declareProperty("PeakRadius", 0,
108 "A value of the peak radius the peak functions should use. A "
109 "peak radius defines an interval on the x axis around the "
110 "centre of the peak where its values are calculated. Values "
111 "outside the interval are not calculated and assumed zeros."
112 "Numerically the radius is a whole number of peak widths "
113 "(FWHM) that fit into the interval on each side from the "
114 "centre. The default value of 0 means the whole x axis.");
115
116 initConcrete();
117}
118
124void IFittingAlgorithm::afterPropertySet(const std::string &propName) {
125 if (propName == "Function") {
126 setFunction();
127 } else if (propName.size() >= 14 && propName.substr(0, 14) == "InputWorkspace") {
128 if (getPointerToProperty("Function")->isDefault()) {
129 throw std::invalid_argument("Function must be set before InputWorkspace");
130 }
131 addWorkspace(propName);
132 } else if (propName == "DomainType") {
134 } else if (propName == "StepSizeMethod") {
136 }
137}
138
143 std::string domainType = getPropertyValue("DomainType");
144 if (domainType == "Simple") {
146 } else if (domainType == "Sequential") {
148 } else if (domainType == "Parallel") {
150 } else {
152 }
153}
154
156 // get the function
157 m_function = getProperty("Function");
158 size_t ndom = m_function->getNumberDomains();
159 if (ndom > 1) {
160 m_workspacePropertyNames.resize(ndom);
162 m_workspacePropertyNames[0] = "InputWorkspace";
163 m_workspaceIndexPropertyNames[0] = "WorkspaceIndex";
164 for (size_t i = 1; i < ndom; ++i) {
165 std::string workspacePropertyName = "InputWorkspace_" + std::to_string(i);
166 m_workspacePropertyNames[i] = workspacePropertyName;
167 std::string workspaceIndexPropertyName = "WorkspaceIndex_" + std::to_string(i);
168 m_workspaceIndexPropertyNames[i] = workspaceIndexPropertyName;
169 if (!existsProperty(workspacePropertyName)) {
170 declareProperty(std::make_unique<API::WorkspaceProperty<API::Workspace>>(workspacePropertyName, "",
172 "Name of the input Workspace");
173 }
174 }
175 } else {
176 m_workspacePropertyNames.resize(1, "InputWorkspace");
177 m_workspaceIndexPropertyNames.resize(1, "WorkspaceIndex");
178 }
179}
180
185 if (m_function) {
186 const std::string stepSizeMethod = getProperty("StepSizeMethod");
187 m_function->setStepSizeMethod(stepSizeMethod == "Sqrt epsilon" ? IFunction::StepSizeMethod::SQRT_EPSILON
188 : IFunction::StepSizeMethod::DEFAULT);
189 }
190}
191
200void IFittingAlgorithm::addWorkspace(const std::string &workspacePropertyName, bool addProperties) {
201 // m_function->setWorkspace(ws);
202 const size_t n = std::string("InputWorkspace").size();
203 const std::string suffix = (workspacePropertyName.size() > n) ? workspacePropertyName.substr(n) : "";
204 const size_t index = suffix.empty() ? 0 : boost::lexical_cast<size_t>(suffix.substr(1));
205
206 IFunction_sptr fun = getProperty("Function");
208
209 auto creator = createDomainCreator(fun.get(), workspacePropertyName, this, m_domainType);
210
211 if (!m_domainCreator) {
212 if (m_workspacePropertyNames.empty()) {
213 // this defines the function and fills in m_workspacePropertyNames with
214 // names of the sort InputWorkspace_#
215 setFunction();
216 }
217 if (fun->getNumberDomains() > 1) {
218 auto multiCreator = std::make_shared<MultiDomainCreator>(this, m_workspacePropertyNames);
219 creator->declareDatasetProperties(suffix, addProperties);
220 multiCreator->setCreator(index, creator.release());
221 m_domainCreator = multiCreator;
222 } else {
223 creator->declareDatasetProperties(suffix, addProperties);
224 m_domainCreator.reset(creator.release());
225 }
226 } else {
227 if (fun->getNumberDomains() > 1) {
228 std::shared_ptr<MultiDomainCreator> multiCreator = std::dynamic_pointer_cast<MultiDomainCreator>(m_domainCreator);
229 if (!multiCreator) {
230 auto const &reference = *m_domainCreator;
231 throw std::runtime_error(std::string("MultiDomainCreator expected, found ") + typeid(reference).name());
232 }
233 if (!multiCreator->hasCreator(index)) {
234 creator->declareDatasetProperties(suffix, addProperties);
235 }
236 multiCreator->setCreator(index, creator.release());
237 } else {
238 creator->declareDatasetProperties(suffix, addProperties);
239 }
240 }
241}
242
249 if (m_function->getNumberDomains() > 1) {
251 }
252 auto props = getProperties();
253 for (auto &prop : props) {
254 if ((*prop).direction() == Kernel::Direction::Input && dynamic_cast<API::IWorkspaceProperty *>(prop)) {
255 const std::string workspacePropertyName = (*prop).name();
256 auto creator = createDomainCreator(m_function.get(), workspacePropertyName, this, m_domainType);
257
258 const size_t n = std::string("InputWorkspace").size();
259 const std::string suffix = (workspacePropertyName.size() > n) ? workspacePropertyName.substr(n) : "";
260 const size_t index = suffix.empty() ? 0 : boost::lexical_cast<size_t>(suffix.substr(1));
261 creator->declareDatasetProperties(suffix, false);
262 if (!m_domainCreator) {
263 m_domainCreator.reset(creator.release());
264 }
265 auto multiCreator = std::dynamic_pointer_cast<MultiDomainCreator>(m_domainCreator);
266 if (multiCreator) {
267 multiCreator->setCreator(index, creator.release());
268 }
269 }
270 }
271
272 // If domain creator wasn't created it's probably because
273 // InputWorkspace property was deleted. Try without the workspace
274 if (!m_domainCreator) {
275 auto creator = createDomainCreator(m_function.get(), "", this, m_domainType);
276 creator->declareDatasetProperties("", true);
277 m_domainCreator.reset(creator.release());
280 }
281}
282
285std::vector<std::string> IFittingAlgorithm::getCostFunctionNames() const {
286 std::vector<std::string> out;
287 auto &factory = CostFunctionFactory::Instance();
288 auto names = factory.getKeys();
289 out.reserve(names.size());
290 for (auto &name : names) {
291 if (std::dynamic_pointer_cast<CostFunctions::CostFuncFitting>(factory.create(name))) {
292 out.emplace_back(name);
293 }
294 }
295 return out;
296}
297
300 Kernel::IValidator_sptr costFuncValidator =
301 std::make_shared<Kernel::ListValidator<std::string>>(getCostFunctionNames());
302 declareProperty("CostFunction", "Least squares", costFuncValidator,
303 "The cost function to be used for the fit, default is Least squares", Kernel::Direction::InOut);
304}
305
308std::shared_ptr<CostFunctions::CostFuncFitting> IFittingAlgorithm::getCostFunctionInitialized() const {
309 // Function may need some preparation.
310 m_function->sortTies();
311 m_function->setUpForFit();
312
315 const bool ignoreInvalidData = getProperty("IgnoreInvalidData");
316 m_domainCreator->ignoreInvalidData(ignoreInvalidData);
317 m_domainCreator->createDomain(domain, values);
318
319 // Set peak radius to the values which will be passed to
320 // all IPeakFunctions
321 int peakRadius = getProperty("PeakRadius");
322 if (auto d1d = dynamic_cast<API::FunctionDomain1D *>(domain.get())) {
323 if (peakRadius != 0) {
324 d1d->setPeakRadius(peakRadius);
325 }
326 }
327
328 // Do something with the function which may depend on workspace.
329 m_domainCreator->initFunction(m_function);
330
331 // get the cost function which must be a CostFuncFitting
332 auto costFunction = std::dynamic_pointer_cast<CostFunctions::CostFuncFitting>(
333 API::CostFunctionFactory::Instance().create(getPropertyValue("CostFunction")));
334
335 costFunction->setIgnoreInvalidData(ignoreInvalidData);
336 costFunction->setFittingFunction(m_function, domain, values);
337
338 return costFunction;
339}
340
341//----------------------------------------------------------------------------------------------
344 if (!m_domainCreator) {
345 setFunction();
347 }
348 m_domainCreator->ignoreInvalidData(getProperty("IgnoreInvalidData"));
349 // Execute the concrete algorithm.
350 this->execConcrete();
351}
352
353} // namespace Mantid::CurveFitting
std::map< DeltaEMode::Type, std::string > index
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
bool existsProperty(const std::string &name) const override
Checks whether the named property is already in the list of managed property.
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.
bool isDefault(const std::string &name) const
const std::vector< Kernel::Property * > & getProperties() const override
Get the list of managed properties.
const std::string name() const override=0
function to return a name of the algorithm, must be overridden in all algorithms
Represent a domain for functions of one real argument.
DomainType
Type of domain to create.
IFunctionGeneral: a very general function definition.
This is a specialization of IFunction for functions defined on an IMDWorkspace.
Definition IFunctionMD.h:50
This is an interface to a fitting function - a semi-abstarct class.
Definition IFunction.h:166
An interface that is implemented by WorkspaceProperty.
A property class for workspaces.
void declareCostFunctionProperty()
Declare a "CostFunction" property.
virtual void execConcrete()=0
Child classes implement the algorithm logic here.
void setDomainType()
Read domain type property and cache the value.
std::shared_ptr< API::IDomainCreator > m_domainCreator
Pointer to a domain creator.
void addWorkspace(const std::string &workspacePropertyName, bool addProperties=true)
Add a new workspace to the fit.
const std::string category() const override
Algorithm's category for identification.
std::vector< std::string > m_workspacePropertyNames
std::shared_ptr< CostFunctions::CostFuncFitting > getCostFunctionInitialized() const
Create a cost function from the "CostFunction" property and make it ready for evaluation.
void exec() override
Execute the algorithm.
std::shared_ptr< API::IFunction > m_function
Pointer to the fitting function.
std::vector< std::string > getCostFunctionNames() const
Return names of registered cost function for CostFuncFitting dynamic type.
void init() override
Initialize the algorithm's properties.
void setStepSizeMethod()
Sets the method to use when calculating the step size for the numerical derivative.
virtual void initConcrete()=0
Child classes declare their properties here.
void afterPropertySet(const std::string &propName) override
Examine "Function" and "InputWorkspace" properties to decide which domain creator to use.
API::IDomainCreator::DomainType m_domainType
Keep the domain type.
void addWorkspaces()
Collect all input workspace property names in the m_workspacePropertyNames vector.
std::vector< std::string > m_workspaceIndexPropertyNames
Interface to PropertyManager.
virtual TypedValue getProperty(const std::string &name) const =0
Get the value of a property.
virtual std::string getPropertyValue(const std::string &name) const =0
Get the value of a property as a string.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
std::shared_ptr< FunctionValues > FunctionValues_sptr
typedef for a shared pointer
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
Definition IFunction.h:743
std::shared_ptr< FunctionDomain > FunctionDomain_sptr
typedef for a shared pointer
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
std::string to_string(const wide_integer< Bits, Signed > &n)
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53