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 &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 m_domainCreator->ignoreInvalidData(getProperty("IgnoreInvalidData"));
316 m_domainCreator->createDomain(domain, values);
317
318 // Set peak radius to the values which will be passed to
319 // all IPeakFunctions
320 int peakRadius = getProperty("PeakRadius");
321 if (auto d1d = dynamic_cast<API::FunctionDomain1D *>(domain.get())) {
322 if (peakRadius != 0) {
323 d1d->setPeakRadius(peakRadius);
324 }
325 }
326
327 // Do something with the function which may depend on workspace.
328 m_domainCreator->initFunction(m_function);
329
330 // get the cost function which must be a CostFuncFitting
331 auto costFunction = std::dynamic_pointer_cast<CostFunctions::CostFuncFitting>(
333
334 costFunction->setFittingFunction(m_function, domain, values);
335
336 return costFunction;
337}
338
339//----------------------------------------------------------------------------------------------
342 if (!m_domainCreator) {
343 setFunction();
345 }
346 m_domainCreator->ignoreInvalidData(getProperty("IgnoreInvalidData"));
347 // Execute the concrete algorithm.
348 this->execConcrete();
349}
350
351} // namespace Mantid::CurveFitting
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
Definition: Algorithm.cpp:2033
bool existsProperty(const std::string &name) const override
Checks whether the named property is already in the list of managed property.
Definition: Algorithm.cpp:2008
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
bool isDefault(const std::string &name) const
Definition: Algorithm.cpp:2084
const std::vector< Kernel::Property * > & getProperties() const override
Get the list of managed properties.
Definition: Algorithm.cpp:2050
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:163
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...
Definition: ListValidator.h:29
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< FunctionValues > FunctionValues_sptr
typedef for a shared pointer
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< 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