Mantid
Loading...
Searching...
No Matches
QENSFitSequential.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
10#include "MantidAPI/Axis.h"
14#include "MantidAPI/IFunction.h"
20
21#include <boost/cast.hpp>
22#include <boost/regex.hpp>
23
24#include <sstream>
25#include <stdexcept>
26#include <unordered_map>
27#include <utility>
28
29namespace {
30using namespace Mantid::API;
31using namespace Mantid::Kernel;
32
33WorkspaceGroup_sptr getADSGroupWorkspace(const std::string &workspaceName) {
34 return AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(workspaceName);
35}
36
37MatrixWorkspace_sptr getADSMatrixWorkspace(const std::string &workspaceName) {
38 return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(workspaceName);
39}
40
41MatrixWorkspace_sptr convertSpectrumAxis(const MatrixWorkspace_sptr &inputWorkspace, const std::string &outputName) {
42 auto convSpec = AlgorithmManager::Instance().create("ConvertSpectrumAxis");
43 convSpec->setLogging(false);
44 convSpec->setProperty("InputWorkspace", inputWorkspace);
45 convSpec->setProperty("OutputWorkspace", outputName);
46 convSpec->setProperty("Target", "ElasticQ");
47 convSpec->setProperty("EMode", "Indirect");
48 convSpec->execute();
49 // Attempting to use getProperty("OutputWorkspace") on algorithm results in a
50 // nullptr being returned
51 return getADSMatrixWorkspace(outputName);
52}
53
54MatrixWorkspace_sptr cloneWorkspace(const MatrixWorkspace_sptr &inputWorkspace, const std::string &outputName) {
55 Workspace_sptr workspace = inputWorkspace->clone();
56 AnalysisDataService::Instance().addOrReplace(outputName, workspace);
57 return std::dynamic_pointer_cast<MatrixWorkspace>(workspace);
58}
59
60MatrixWorkspace_sptr convertToElasticQ(const MatrixWorkspace_sptr &inputWorkspace, const std::string &outputName,
61 bool doThrow) {
62 auto axis = inputWorkspace->getAxis(1);
63 if (axis->isSpectra())
64 return convertSpectrumAxis(inputWorkspace, outputName);
65 else if (axis->isNumeric()) {
66 if (axis->unit()->unitID() != "MomentumTransfer" && doThrow)
67 throw std::runtime_error("Input must have axis values of Q");
68 return cloneWorkspace(inputWorkspace, outputName);
69 } else if (doThrow)
70 throw std::runtime_error("Input workspace must have either spectra or numeric axis.");
71 return cloneWorkspace(inputWorkspace, outputName);
72}
73
74struct ElasticQAppender {
75 explicit ElasticQAppender(std::vector<MatrixWorkspace_sptr> &elasticInput)
76 : m_elasticInput(elasticInput), m_converted() {}
77
78 void operator()(const MatrixWorkspace_sptr &workspace, const std::string &outputBase, bool doThrow) {
79 auto it = m_converted.find(workspace.get());
80 if (it != m_converted.end())
81 m_elasticInput.emplace_back(it->second);
82 else {
83 auto elasticQ = convertToElasticQ(workspace, outputBase + std::to_string(m_converted.size() + 1), doThrow);
84 m_elasticInput.emplace_back(elasticQ);
85 m_converted[workspace.get()] = elasticQ;
86 }
87 }
88
89private:
90 std::vector<MatrixWorkspace_sptr> &m_elasticInput;
91 std::unordered_map<MatrixWorkspace *, MatrixWorkspace_sptr> m_converted;
92};
93
94std::vector<MatrixWorkspace_sptr> convertToElasticQ(const std::vector<MatrixWorkspace_sptr> &workspaces,
95 const std::string &outputBaseName, bool doThrow) {
96 std::vector<MatrixWorkspace_sptr> elasticInput;
97 auto appendElasticQWorkspace = ElasticQAppender(elasticInput);
98 appendElasticQWorkspace(workspaces[0], outputBaseName, doThrow);
99
100 for (auto i = 1u; i < workspaces.size(); ++i)
101 appendElasticQWorkspace(workspaces[i], outputBaseName, doThrow);
102 return elasticInput;
103}
104
105void extractFunctionNames(const CompositeFunction_sptr &composite, std::vector<std::string> &names) {
106 for (auto i = 0u; i < composite->nFunctions(); ++i)
107 names.emplace_back(composite->getFunction(i)->name());
108}
109
110void extractFunctionNames(const IFunction_sptr &function, std::vector<std::string> &names) {
111 auto composite = std::dynamic_pointer_cast<CompositeFunction>(function);
112 if (composite)
113 extractFunctionNames(composite, names);
114 else
115 names.emplace_back(function->name());
116}
117
118void extractConvolvedNames(const IFunction_sptr &function, std::vector<std::string> &names);
119
120void extractConvolvedNames(const CompositeFunction_sptr &composite, std::vector<std::string> &names) {
121 for (auto i = 0u; i < composite->nFunctions(); ++i)
122 extractConvolvedNames(composite->getFunction(i), names);
123}
124
125void extractConvolvedNames(const IFunction_sptr &function, std::vector<std::string> &names) {
126 auto composite = std::dynamic_pointer_cast<CompositeFunction>(function);
127 if (composite) {
128 if (composite->name() == "Convolution" && composite->nFunctions() > 1 &&
129 composite->getFunction(0)->name() == "Resolution")
130 extractFunctionNames(composite->getFunction(1), names);
131 else
132 extractConvolvedNames(composite, names);
133 }
134}
135
136std::string constructInputString(const MatrixWorkspace_sptr &workspace, int specMin, int specMax) {
137 std::ostringstream input;
138 for (auto i = specMin; i < specMax + 1; ++i)
139 input << workspace->getName() << ",i" << std::to_string(i) << ";";
140 return input.str();
141}
142
143std::vector<std::string> extractWorkspaceNames(const std::string &input) {
144 std::vector<std::string> v;
145 boost::regex reg("([^,;]+),");
146 std::for_each(boost::sregex_token_iterator(input.begin(), input.end(), reg, 1), boost::sregex_token_iterator(),
147 [&v](const std::string &name) { v.emplace_back(name); });
148 return v;
149}
150
151std::vector<std::string> getUniqueWorkspaceNames(const std::string &input) {
152 auto workspaceNames = extractWorkspaceNames(input);
153 std::set<std::string> uniqueNames(workspaceNames.begin(), workspaceNames.end());
154 workspaceNames.assign(uniqueNames.begin(), uniqueNames.end());
155 return workspaceNames;
156}
157
158std::vector<MatrixWorkspace_sptr> extractWorkspaces(const std::string &input) {
159 const auto workspaceNames = extractWorkspaceNames(input);
160
161 std::vector<MatrixWorkspace_sptr> workspaces;
162
163 std::transform(workspaceNames.begin(), workspaceNames.end(), std::back_inserter(workspaces),
164 [](const auto &wsName) { return getADSMatrixWorkspace(wsName); });
165
166 return workspaces;
167}
168
169std::vector<std::string> getSpectra(const std::string &input) {
170 std::vector<std::string> spectra;
171 boost::regex reg(",[i|sp](0|[1-9][0-9]*);?");
172 std::copy(boost::sregex_token_iterator(input.begin(), input.end(), reg, 1), boost::sregex_token_iterator(),
173 std::back_inserter(spectra));
174 return spectra;
175}
176
177std::vector<std::string> getSuffices(const std::string &input) {
178 std::vector<std::string> suffices;
179 boost::regex reg(",[i|sp](0|[1-9][0-9]*);?");
180 std::copy(boost::sregex_token_iterator(input.begin(), input.end(), reg, 0), boost::sregex_token_iterator(),
181 std::back_inserter(suffices));
182 return suffices;
183}
184
185std::string replaceWorkspaces(const std::string &input, const std::vector<MatrixWorkspace_sptr> &workspaces) {
186 const auto suffices = getSuffices(input);
187 std::stringstream newInput;
188 for (auto i = 0u; i < workspaces.size(); ++i)
189 newInput << workspaces[i]->getName() << suffices[i];
190 return newInput.str();
191}
192
193void renameWorkspace(const Algorithm_sptr &renamer, const Workspace_sptr &workspace, const std::string &newName) {
194 renamer->setProperty("InputWorkspace", workspace);
195 renamer->setProperty("OutputWorkspace", newName);
196 renamer->executeAsChildAlg();
197}
198
199void deleteTemporaries(const Algorithm_sptr &deleter, const std::string &base) {
200 auto name = base + std::to_string(1);
201 std::size_t i = 2;
202
203 while (AnalysisDataService::Instance().doesExist(name)) {
204 deleter->setProperty("Workspace", name);
205 deleter->executeAsChildAlg();
206 name = base + std::to_string(i++);
207 }
208}
209
210std::string shortParameterName(const std::string &longName) {
211 return longName.substr(longName.rfind('.') + 1, longName.size());
212}
213
214bool containsMultipleData(const std::vector<MatrixWorkspace_sptr> &workspaces) {
215 const auto &first = workspaces.front();
216 return std::any_of(workspaces.cbegin(), workspaces.cend(),
217 [&first](const auto &workspace) { return workspace != first; });
218}
219
220template <typename F, typename Renamer>
221void renameWorkspacesWith(const WorkspaceGroup_sptr &groupWorkspace, F const &getName, Renamer const &renamer) {
222 std::unordered_map<std::string, std::size_t> nameCount;
223 for (auto i = 0u; i < groupWorkspace->size(); ++i) {
224 const auto name = getName(i);
225 auto count = nameCount.find(name);
226
227 if (count == nameCount.end()) {
228 renamer(groupWorkspace->getItem(i), name);
229 nameCount[name] = 1;
230 } else
231 renamer(groupWorkspace->getItem(i), name + "(" + std::to_string(++count->second) + ")");
232 }
233}
234
235template <typename F>
236void renameWorkspacesInQENSFit(Algorithm *qensFit, const Algorithm_sptr &renameAlgorithm,
237 WorkspaceGroup_sptr outputGroup, std::string const &outputBaseName,
238 std::string const &groupSuffix, const F &getNameSuffix) {
239 Progress renamerProg(qensFit, 0.98, 1.0, outputGroup->size() + 1);
240 renamerProg.report("Renaming group workspaces...");
241
242 auto getName = [&](std::size_t i) { return outputBaseName + "_" + getNameSuffix(i); };
243
244 auto renamer = [&](const Workspace_sptr &workspace, const std::string &name) {
245 renameWorkspace(renameAlgorithm, workspace, name);
246 renamerProg.report("Renamed workspace in group.");
247 };
248 renameWorkspacesWith(outputGroup, getName, renamer);
249
250 auto const groupName = outputBaseName + groupSuffix;
251 if (outputGroup->getName() != groupName)
252 renameWorkspace(renameAlgorithm, outputGroup, groupName);
253}
254
255std::vector<std::size_t> createDatasetGrouping(const std::vector<MatrixWorkspace_sptr> &workspaces,
256 std::size_t maximum) {
257 std::vector<std::size_t> grouping;
258 grouping.emplace_back(0);
259 for (auto i = 1u; i < workspaces.size(); ++i) {
260 if (workspaces[i] != workspaces[i - 1])
261 grouping.emplace_back(i);
262 }
263 grouping.emplace_back(maximum);
264 return grouping;
265}
266
267std::vector<std::size_t> createDatasetGrouping(const std::vector<MatrixWorkspace_sptr> &workspaces) {
268 return createDatasetGrouping(workspaces, workspaces.size());
269}
270
271WorkspaceGroup_sptr createGroup(const std::vector<MatrixWorkspace_sptr> &workspaces) {
273 for (auto &&workspace : workspaces)
274 group->addWorkspace(workspace);
275 return group;
276}
277
278WorkspaceGroup_sptr runParameterProcessingWithGrouping(Algorithm &processingAlgorithm,
279 const std::vector<std::size_t> &grouping) {
280 std::vector<MatrixWorkspace_sptr> results;
281 results.reserve(grouping.size() - 1);
282 for (auto i = 0u; i < grouping.size() - 1; ++i) {
283 processingAlgorithm.setProperty("StartRowIndex", static_cast<int>(grouping[i]));
284 processingAlgorithm.setProperty("EndRowIndex", static_cast<int>(grouping[i + 1]) - 1);
285 processingAlgorithm.setProperty("OutputWorkspace", "__Result");
286 processingAlgorithm.execute();
287 results.emplace_back(processingAlgorithm.getProperty("OutputWorkspace"));
288 }
289 return createGroup(results);
290}
291
292} // namespace
293
295
296using namespace API;
297using namespace Kernel;
298
299// Register the algorithm into the AlgorithmFactory
300DECLARE_ALGORITHM(QENSFitSequential)
301
302
303const std::string QENSFitSequential::name() const { return "QENSFitSequential"; }
304
306int QENSFitSequential::version() const { return 1; }
307
309const std::string QENSFitSequential::category() const { return "Workflow\\MIDAS"; }
310
312const std::string QENSFitSequential::summary() const { return "Performs a sequential fit for QENS data"; }
313
315const std::vector<std::string> QENSFitSequential::seeAlso() const {
316 return {"ConvolutionFitSequential", "IqtFitSequential", "PlotPeakByLogValue"};
317}
318
319void QENSFitSequential::init() {
320 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, PropertyMode::Optional),
321 "The input workspace for the fit. This property will be ignored if "
322 "'Input' is provided.");
323
324 auto boundedV = std::make_shared<BoundedValidator<int>>();
325 boundedV->setLower(0);
326
327 declareProperty("SpecMin", 0, boundedV,
328 "The first spectrum to be used in "
329 "the fit. Spectra values can not be "
330 "negative. This property will be ignored if 'Input' is provided.",
332
333 declareProperty("SpecMax", 0, boundedV,
334 "The final spectrum to be used in "
335 "the fit. Spectra values can not be "
336 "negative. This property will be ignored if 'Input' is provided.",
338
339 declareProperty("Input", "",
340 "A list of sources of data to fit. \n"
341 "Sources can be either workspace names or file names followed optionally "
342 "by a list of spectra/workspace-indices \n"
343 "or values using the notation described in the description section of "
344 "the help page.");
345
346 std::vector<std::string> unitOptions = UnitFactory::Instance().getKeys();
347 unitOptions.emplace_back("");
348 declareProperty("ResultXAxisUnit", "MomentumTransfer", std::make_shared<StringListValidator>(unitOptions),
349 "The unit to assign to the X Axis of the result workspace, "
350 "defaults to MomentumTransfer");
351
352 declareProperty(std::make_unique<WorkspaceProperty<WorkspaceGroup>>("OutputWorkspace", "", Direction::Output),
353 "The output result workspace(s)");
354 declareProperty(std::make_unique<WorkspaceProperty<ITableWorkspace>>("OutputParameterWorkspace", "",
356 "The output parameter workspace");
357 declareProperty(std::make_unique<WorkspaceProperty<WorkspaceGroup>>("OutputWorkspaceGroup", "", Direction::Output,
359 "The output group workspace");
360
361 declareProperty(std::make_unique<FunctionProperty>("Function", Direction::InOut),
362 "The fitting function, common for all workspaces in the input.");
363 declareProperty("LogName", "axis-1",
364 "Name of the log value to plot the "
365 "parameters against. Default: use spectra "
366 "numbers.");
367 declareProperty(std::make_unique<ArrayProperty<double>>("StartX"), "A value of x in, or on the low x "
368 "boundary of, the first bin to "
369 "include in\n"
370 "the fit (default lowest value of x)");
371 declareProperty(std::make_unique<ArrayProperty<double>>("EndX"), "A value in, or on the high x boundary "
372 "of, the last bin the fitting range\n"
373 "(default the highest value of x)");
374
375 declareProperty("PassWSIndexToFunction", false,
376 "For each spectrum in Input pass its workspace index to all "
377 "functions that"
378 "have attribute WorkspaceIndex.");
379
380 declareProperty("Minimizer", "Levenberg-Marquardt",
381 "Minimizer to use for fitting. Minimizers available are "
382 "'Levenberg-Marquardt', 'Simplex', 'FABADA',\n"
383 "'Conjugate gradient (Fletcher-Reeves imp.)', 'Conjugate "
384 "gradient (Polak-Ribiere imp.)' and 'BFGS'");
385
386 const std::vector<std::string> costFuncOptions = CostFunctionFactory::Instance().getKeys();
387 declareProperty("CostFunction", "Least squares", std::make_shared<StringListValidator>(costFuncOptions),
388 "Cost functions to use for fitting. Cost functions available "
389 "are 'Least squares' and 'Ignore positive peaks'",
391
392 declareProperty("MaxIterations", 500, boundedV,
393 "Stop after this number of iterations if a good fit is not "
394 "found");
395 declareProperty("PeakRadius", 0,
396 "A value of the peak radius the peak functions should use. A "
397 "peak radius defines an interval on the x axis around the "
398 "centre of the peak where its values are calculated. Values "
399 "outside the interval are not calculated and assumed zeros."
400 "Numerically the radius is a whole number of peak widths "
401 "(FWHM) that fit into the interval on each side from the "
402 "centre. The default value of 0 means the whole x axis.");
403
404 declareProperty("ExtractMembers", false,
405 "If true, then each member of the fit will be extracted"
406 ", into their own workspace. These workspaces will have a histogram"
407 " for each spectrum (Q-value) and will be grouped.",
409
410 declareProperty("OutputCompositeMembers", false,
411 "If true and CreateOutput is true then the value of each "
412 "member of a Composite Function is also output.");
413
414 declareProperty(std::make_unique<Kernel::PropertyWithValue<bool>>("ConvolveMembers", false),
415 "If true and OutputCompositeMembers is true members of any "
416 "Convolution are output convolved\n"
417 "with corresponding resolution");
418
419 const std::array<std::string, 2> evaluationTypes = {{"CentrePoint", "Histogram"}};
420 declareProperty("EvaluationType", "CentrePoint",
422 "The way the function is evaluated: CentrePoint or Histogram.", Kernel::Direction::Input);
423
424 const std::array<std::string, 2> fitTypes = {{"Sequential", "Individual"}};
425 declareProperty("FitType", "Sequential", Kernel::IValidator_sptr(new Kernel::ListValidator<std::string>(fitTypes)),
426 "Defines the way of setting initial values. If set to Sequential every "
427 "next fit starts with parameters returned by the previous fit. If set to "
428 "Individual each fit starts with the same initial values defined in "
429 "the Function property. Allowed values: [Sequential, Individual]",
431
432 declareProperty(std::make_unique<ArrayProperty<double>>("Exclude", ""),
433 "A list of pairs of real numbers, defining the regions to "
434 "exclude from the fit.");
435
436 declareProperty(std::make_unique<ArrayProperty<std::string>>("ExcludeMultiple", ""),
437 "A list of Exclusion ranges, defining the regions to "
438 "exclude from the fit for each spectra. Must have the "
439 "same number of sets as the number of the spectra.");
440
441 declareProperty("IgnoreInvalidData", false, "Flag to ignore infinities, NaNs and data with zero errors.");
442
443 declareProperty("OutputFitStatus", false,
444 "Flag to output fit status information, which consists of the fit "
445 "OutputStatus and the OutputChiSquared");
446}
447
448std::map<std::string, std::string> QENSFitSequential::validateInputs() {
449 std::map<std::string, std::string> errors;
450
451 if (getPropertyValue("Input").empty()) {
452 MatrixWorkspace_sptr workspace = getProperty("InputWorkspace");
453 if (!workspace)
454 errors["InputWorkspace"] = "No input string or input workspace was provided.";
455
456 const int specMin = getProperty("SpecMin");
457 const int specMax = getProperty("SpecMax");
458 if (specMin > specMax)
459 errors["SpecMin"] = "SpecMin must be less than or equal to SpecMax.";
460 }
461 const auto inputWorkspaces = getWorkspaces();
462 const auto workspaces = convertInputToElasticQ(inputWorkspaces);
463 const auto inputString = getInputString(workspaces);
464 const auto spectra = getSpectra(inputString);
465 const std::vector<double> startX = getProperty("StartX");
466 const std::vector<double> endX = getProperty("EndX");
467 if (startX.size() != endX.size()) {
468 errors["StartX"] = "StartX have the same size as EndX";
469 } else if (startX.size() != spectra.size() && startX.size() != 1) {
470 errors["StartX"] = "StartX must be a single value or have a value for each spectra.";
471 } else {
472 for (size_t i = 0; i < startX.size(); i++) {
473 if (startX[i] >= endX[i]) {
474 errors["StartX"] = "StartX must be less than EndX";
475 }
476 }
477 }
478 return errors;
479}
480
481void QENSFitSequential::exec() {
482 const auto outputBaseName = getOutputBaseName();
483
484 if (getPropertyValue("OutputParameterWorkspace").empty())
485 setProperty("OutputParameterWorkspace", outputBaseName + "_Parameters");
486
487 if (getPropertyValue("OutputWorkspaceGroup").empty())
488 setProperty("OutputWorkspaceGroup", outputBaseName + "_Workspaces");
489
490 const auto inputWorkspaces = getWorkspaces();
491 const auto workspaces = convertInputToElasticQ(inputWorkspaces);
492 const auto inputString = getInputString(workspaces);
493 const auto spectra = getSpectra(inputString);
494
495 if (workspaces.empty() || spectra.empty() || (workspaces.size() > 1 && workspaces.size() != spectra.size()))
496 throw std::invalid_argument("A malformed input string was provided.");
497
498 const auto parameterWs = processParameterTable(performFit(inputString, outputBaseName));
499 const auto resultWs = processIndirectFitParameters(parameterWs, getDatasetGrouping(workspaces));
500 const auto groupWs = getADSGroupWorkspace(outputBaseName + "_Workspaces");
501 AnalysisDataService::Instance().addOrReplace(getPropertyValue("OutputWorkspace"), resultWs);
502
503 if (containsMultipleData(workspaces)) {
504 const auto inputStringProp = getPropertyValue("Input");
505 renameWorkspaces(groupWs, spectra, outputBaseName, "_Workspace", extractWorkspaceNames(inputStringProp));
506 auto inputWorkspaceNames = getUniqueWorkspaceNames(inputStringProp);
507 renameWorkspaces(resultWs, std::vector<std::string>(inputWorkspaceNames.size(), ""), outputBaseName, "_Result",
508 inputWorkspaceNames);
509 } else {
510 renameWorkspaces(groupWs, spectra, outputBaseName, "_Workspace");
511 renameWorkspaces(resultWs, std::vector<std::string>({""}), outputBaseName, "_Result");
512 }
513
514 copyLogs(resultWs, workspaces);
515
516 const bool doExtractMembers = getProperty("ExtractMembers");
517 if (doExtractMembers)
518 extractMembers(groupWs, workspaces, outputBaseName + "_Members");
519
520 renameGroupWorkspace("__PDF_Workspace", spectra, outputBaseName, "_PDF");
521
522 deleteTemporaryWorkspaces(outputBaseName);
523
524 size_t itter = 0;
525 for (auto results : resultWs->getAllItems()) {
526 addAdditionalLogs(results);
527 std::string resultWsName = results->getName();
528 auto endLoc = resultWsName.find("__Result");
529 std::string baseName = resultWsName.erase(endLoc);
530 for (auto &workspace : groupWs->getAllItems()) {
531 const std::string wsName = workspace->getName();
532 if (wsName.find(baseName) != wsName.npos) {
533 copyLogs(std::dynamic_pointer_cast<MatrixWorkspace>(results), groupWs);
535 itter++;
536 }
537 }
538 addFitRangeLogs(results, itter - 1);
539 }
540
541 setProperty("OutputWorkspace", resultWs);
542 setProperty("OutputParameterWorkspace", parameterWs);
543 // Copy the group to prevent the ADS having two entries for one workspace
544 auto outGroupWs = WorkspaceGroup_sptr(new WorkspaceGroup);
545 for (auto item : groupWs->getAllItems()) {
546 outGroupWs->addWorkspace(item);
547 }
548 setProperty("OutputWorkspaceGroup", outGroupWs);
549}
550
551std::map<std::string, std::string> QENSFitSequential::getAdditionalLogStrings() const {
552 const bool convolve = getProperty("ConvolveMembers");
553 auto fitProgram = name();
554 fitProgram = fitProgram.substr(0, fitProgram.rfind("Sequential"));
555
556 auto logs = std::map<std::string, std::string>();
557 logs["sample_filename"] = getPropertyValue("InputWorkspace");
558 logs["convolve_members"] = convolve ? "true" : "false";
559 logs["fit_program"] = fitProgram;
560 logs["fit_mode"] = "Sequential";
561 return logs;
562}
563
564std::map<std::string, std::string> QENSFitSequential::getAdditionalLogNumbers() const {
565 return std::map<std::string, std::string>();
566}
567
568void QENSFitSequential::addAdditionalLogs(const WorkspaceGroup_sptr &resultWorkspace) {
569 for (const auto &workspace : *resultWorkspace)
571}
572
573void QENSFitSequential::addAdditionalLogs(const Workspace_sptr &resultWorkspace) {
574 auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false);
575 logAdder->setProperty("Workspace", resultWorkspace);
576 Progress logAdderProg(this, 0.99, 1.00, 6);
577 logAdder->setProperty("LogType", "String");
578 for (const auto &log : getAdditionalLogStrings()) {
579 logAdder->setProperty("LogName", log.first);
580 logAdder->setProperty("LogText", log.second);
581 logAdder->executeAsChildAlg();
582 logAdderProg.report("Add text logs");
583 }
584 logAdderProg.report("Add number logs");
585 for (const auto &log : getAdditionalLogNumbers()) {
586 logAdder->setProperty("LogName", log.first);
587 logAdder->setProperty("LogText", log.second);
588 logAdder->executeAsChildAlg();
589 logAdderProg.report("Add number logs");
590 }
591}
592
593void QENSFitSequential::addFitRangeLogs(const API::Workspace_sptr &resultWorkspace, size_t itter) {
594 auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false);
595 logAdder->setProperty("Workspace", resultWorkspace);
596 Progress logAdderProg(this, 0.99, 1.00, 6);
597 logAdder->setProperty("LogType", "String");
598
599 std::vector<double> startX = getProperty("StartX");
600 logAdder->setProperty("LogName", "start_x");
601 if (startX.size() == 1) {
602 logAdder->setProperty("LogText", std::to_string(startX[0]));
603 } else {
604 logAdder->setProperty("LogText", std::to_string(startX[itter]));
605 }
606 logAdder->executeAsChildAlg();
607
608 std::vector<double> endX = getProperty("EndX");
609 logAdder->setProperty("LogName", "end_x");
610 if (endX.size() == 1) {
611 logAdder->setProperty("LogText", std::to_string(endX[0]));
612 } else {
613 logAdder->setProperty("LogText", std::to_string(endX[itter]));
614 }
615 logAdder->executeAsChildAlg();
616}
617
618std::string QENSFitSequential::getOutputBaseName() const {
619 const auto base = getPropertyValue("OutputWorkspace");
620 const auto position = base.rfind("_Result");
621 if (position != std::string::npos)
622 return base.substr(0, position);
623 return base;
624}
625
626bool QENSFitSequential::throwIfElasticQConversionFails() const { return false; }
627
628bool QENSFitSequential::isFitParameter(const std::string & /*unused*/) const { return true; }
629
630std::vector<std::string> QENSFitSequential::getFitParameterNames() const {
631 const auto uniqueParameters = getUniqueParameterNames();
632 std::vector<std::string> parameters;
633 parameters.reserve(uniqueParameters.size());
634 std::copy_if(uniqueParameters.begin(), uniqueParameters.end(), std::back_inserter(parameters),
635 [&](const std::string &parameter) { return isFitParameter(parameter); });
636 return parameters;
637}
638
639std::set<std::string> QENSFitSequential::getUniqueParameterNames() const {
640 IFunction_sptr function = getProperty("Function");
641 std::set<std::string> nameSet;
642 for (auto i = 0u; i < function->nParams(); ++i)
643 nameSet.insert(shortParameterName(function->parameterName(i)));
644 return nameSet;
645}
646
647void QENSFitSequential::deleteTemporaryWorkspaces(const std::string &outputBaseName) {
648 auto deleter = createChildAlgorithm("DeleteWorkspace", -1.0, -1.0, false);
649 deleter->setProperty("Workspace", outputBaseName + "_NormalisedCovarianceMatrices");
650 deleter->executeAsChildAlg();
651
652 deleter->setProperty("Workspace", outputBaseName + "_Parameters");
653 deleter->executeAsChildAlg();
654
655 deleteTemporaries(deleter, getTemporaryName());
656}
657
658std::vector<std::size_t>
659QENSFitSequential::getDatasetGrouping(const std::vector<API::MatrixWorkspace_sptr> &workspaces) const {
660 if (getPropertyValue("Input").empty()) {
661 int maximum = getProperty("SpecMax");
662 return createDatasetGrouping(workspaces, static_cast<std::size_t>(maximum + 1));
663 }
664 return createDatasetGrouping(workspaces);
665}
666
667WorkspaceGroup_sptr QENSFitSequential::processIndirectFitParameters(const ITableWorkspace_sptr &parameterWorkspace,
668 const std::vector<std::size_t> &grouping) {
669 std::string const columnX = getProperty("LogName");
670 std::string const xAxisUnit = getProperty("ResultXAxisUnit");
671 auto pifp = createChildAlgorithm("ProcessIndirectFitParameters", 0.91, 0.95, false);
672 pifp->setAlwaysStoreInADS(false);
673 pifp->setProperty("InputWorkspace", parameterWorkspace);
674 pifp->setProperty("ColumnX", columnX);
675 pifp->setProperty("XAxisUnit", xAxisUnit);
676 pifp->setProperty("ParameterNames", getFitParameterNames());
677 pifp->setProperty("IncludeChiSquared", true);
678 return runParameterProcessingWithGrouping(*pifp, grouping);
679}
680
681ITableWorkspace_sptr QENSFitSequential::processParameterTable(ITableWorkspace_sptr parameterTable) {
682 return parameterTable;
683}
684
685void QENSFitSequential::renameWorkspaces(WorkspaceGroup_sptr outputGroup, std::vector<std::string> const &spectra,
686 std::string const &outputBaseName, std::string const &endOfSuffix,
687 std::vector<std::string> const &inputWorkspaceNames) {
688 auto rename = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false);
689 const auto getNameSuffix = [&](std::size_t i) {
690 std::string workspaceName = inputWorkspaceNames[i] + "_" + spectra[i] + endOfSuffix;
691 return workspaceName;
692 };
693 return renameWorkspacesInQENSFit(this, rename, std::move(outputGroup), outputBaseName, endOfSuffix + "s",
694 getNameSuffix);
695}
696
697void QENSFitSequential::renameWorkspaces(WorkspaceGroup_sptr outputGroup, std::vector<std::string> const &spectra,
698 std::string const &outputBaseName, std::string const &endOfSuffix) {
699 auto rename = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false);
700 auto getNameSuffix = [&](std::size_t i) { return spectra[i] + endOfSuffix; };
701 return renameWorkspacesInQENSFit(this, rename, std::move(outputGroup), outputBaseName, endOfSuffix + "s",
702 getNameSuffix);
703}
704
705void QENSFitSequential::renameGroupWorkspace(std::string const &currentName, std::vector<std::string> const &spectra,
706 std::string const &outputBaseName, std::string const &endOfSuffix) {
707 if (AnalysisDataService::Instance().doesExist(currentName)) {
708 auto const group = getADSGroupWorkspace(currentName);
709 if (group)
710 renameWorkspaces(group, spectra, outputBaseName, endOfSuffix);
711 }
712}
713
714ITableWorkspace_sptr QENSFitSequential::performFit(const std::string &input, const std::string &output) {
715 const std::vector<double> exclude = getProperty("Exclude");
716 const std::vector<std::string> excludeMultiple = getProperty("ExcludeMultiple");
717 const bool convolveMembers = getProperty("ConvolveMembers");
718 const bool outputCompositeMembers = getProperty("OutputCompositeMembers");
719 const bool passWsIndex = getProperty("PassWSIndexToFunction");
720 const bool ignoreInvalidData = getProperty("IgnoreInvalidData");
721 const bool outputFitStatus = getProperty("OutputFitStatus");
722 IFunction_sptr inputFunction = getProperty("Function");
723
724 // Run PlotPeaksByLogValue
725 auto plotPeaks = createChildAlgorithm("PlotPeakByLogValue", 0.05, 0.90, true);
726 plotPeaks->setProperty("Input", input);
727 plotPeaks->setProperty("OutputWorkspace", output);
728 plotPeaks->setProperty("Function", inputFunction);
729 plotPeaks->setProperty("StartX", getPropertyValue("StartX"));
730 plotPeaks->setProperty("EndX", getPropertyValue("EndX"));
731 plotPeaks->setProperty("Exclude", exclude);
732 plotPeaks->setProperty("ExcludeMultiple", excludeMultiple);
733 plotPeaks->setProperty("IgnoreInvalidData", ignoreInvalidData);
734 plotPeaks->setProperty("FitType", "Sequential");
735 plotPeaks->setProperty("CreateOutput", true);
736 plotPeaks->setProperty("OutputCompositeMembers", outputCompositeMembers);
737 plotPeaks->setProperty("ConvolveMembers", convolveMembers);
738 plotPeaks->setProperty("MaxIterations", getPropertyValue("MaxIterations"));
739 plotPeaks->setProperty("Minimizer", getPropertyValue("Minimizer"));
740 plotPeaks->setProperty("PassWSIndexToFunction", passWsIndex);
741 plotPeaks->setProperty("PeakRadius", getPropertyValue("PeakRadius"));
742 plotPeaks->setProperty("LogValue", getPropertyValue("LogName"));
743 plotPeaks->setProperty("EvaluationType", getPropertyValue("EvaluationType"));
744 plotPeaks->setProperty("FitType", getPropertyValue("FitType"));
745 plotPeaks->setProperty("CostFunction", getPropertyValue("CostFunction"));
746 plotPeaks->setProperty("OutputFitStatus", outputFitStatus);
747
748 plotPeaks->executeAsChildAlg();
749
750 if (outputFitStatus) {
751 declareProperty(std::make_unique<ArrayProperty<std::string>>("OutputStatus", Direction::Output));
752 declareProperty(std::make_unique<ArrayProperty<double>>("OutputChiSquared", Direction::Output));
753 std::vector<std::string> outputStatus = plotPeaks->getProperty("OutputStatus");
754 std::vector<double> outputChiSquared = plotPeaks->getProperty("OutputChiSquared");
755 setProperty("OutputStatus", outputStatus);
756 setProperty("OutputChiSquared", outputChiSquared);
757 }
758
759 return plotPeaks->getProperty("OutputWorkspace");
760}
761
762std::string QENSFitSequential::getInputString(const std::vector<MatrixWorkspace_sptr> &workspaces) const {
763 const auto inputString = getPropertyValue("Input");
764 if (!inputString.empty())
765 return replaceWorkspaces(inputString, workspaces);
766 return constructInputString(workspaces[0], getProperty("SpecMin"), getProperty("SpecMax"));
767}
768
769std::vector<MatrixWorkspace_sptr> QENSFitSequential::getWorkspaces() const {
770 const auto inputString = getPropertyValue("Input");
771 if (!inputString.empty()) {
772 auto workspaceList = extractWorkspaces(inputString);
773 return workspaceList;
774 }
775 // The static_cast should not be necessary but it is required to avoid a
776 // "internal compiler error: segmentation fault" when compiling with gcc
777 // and std=c++1z
778 return std::vector<MatrixWorkspace_sptr>{static_cast<MatrixWorkspace_sptr>(getProperty("InputWorkspace"))};
779}
780
781std::vector<MatrixWorkspace_sptr>
782QENSFitSequential::convertInputToElasticQ(const std::vector<MatrixWorkspace_sptr> &workspaces) const {
783 return convertToElasticQ(workspaces, getTemporaryName(), throwIfElasticQConversionFails());
784}
785
786void QENSFitSequential::extractMembers(const WorkspaceGroup_sptr &resultGroupWs,
787 const std::vector<API::MatrixWorkspace_sptr> &workspaces,
788 const std::string &outputWsName) {
789 std::vector<std::string> workspaceNames;
790 std::transform(workspaces.begin(), workspaces.end(), std::back_inserter(workspaceNames),
791 [](const API::MatrixWorkspace_sptr &workspace) { return workspace->getName(); });
792
793 auto extractAlgorithm = extractMembersAlgorithm(resultGroupWs, outputWsName);
794 extractAlgorithm->setProperty("InputWorkspaces", workspaceNames);
795 extractAlgorithm->execute();
796}
797
798void QENSFitSequential::copyLogs(const WorkspaceGroup_sptr &resultWorkspaces,
799 std::vector<MatrixWorkspace_sptr> const &workspaces) {
800 for (auto const &resultWorkspace : *resultWorkspaces)
801 copyLogs(resultWorkspace, workspaces);
802}
803
804void QENSFitSequential::copyLogs(const Workspace_sptr &resultWorkspace,
805 std::vector<MatrixWorkspace_sptr> const &workspaces) {
806 auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false);
807 logCopier->setProperty("OutputWorkspace", resultWorkspace->getName());
808 for (auto const &workspace : workspaces) {
809 logCopier->setProperty("InputWorkspace", workspace);
810 logCopier->executeAsChildAlg();
811 }
812}
813
814void QENSFitSequential::copyLogs(const MatrixWorkspace_sptr &resultWorkspace, const WorkspaceGroup_sptr &resultGroup) {
815 for (auto const &workspace : *resultGroup)
816 copyLogs(resultWorkspace, workspace);
817}
818
819void QENSFitSequential::copyLogs(const MatrixWorkspace_sptr &resultWorkspace, const Workspace_sptr &resultGroup) {
820 auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false);
821 logCopier->setProperty("InputWorkspace", resultWorkspace);
822 logCopier->setProperty("OutputWorkspace", resultGroup->getName());
823 logCopier->executeAsChildAlg();
824}
825
826IAlgorithm_sptr QENSFitSequential::extractMembersAlgorithm(const WorkspaceGroup_sptr &resultGroupWs,
827 const std::string &outputWsName) const {
828 const bool convolved = getProperty("ConvolveMembers");
829 std::vector<std::string> convolvedMembers;
830 IFunction_sptr function = getProperty("Function");
831
832 if (convolved)
833 extractConvolvedNames(function, convolvedMembers);
834
835 auto extractMembersAlg = AlgorithmManager::Instance().create("ExtractQENSMembers");
836 extractMembersAlg->setProperty("ResultWorkspace", resultGroupWs);
837 extractMembersAlg->setProperty("OutputWorkspace", outputWsName);
838 extractMembersAlg->setProperty("RenameConvolvedMembers", convolved);
839 extractMembersAlg->setProperty("ConvolvedMembers", convolvedMembers);
840 return extractMembersAlg;
841}
842
843std::string QENSFitSequential::getTemporaryName() const { return "__" + name() + "_ws"; }
844
845} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double position
Definition: GetAllEi.cpp:154
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
int count
counter
Definition: Matrix.cpp:37
std::string getName(const IMDDimension &self)
Base class from which all concrete algorithm classes should be derived.
Definition: Algorithm.h:85
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
bool execute() override final
The actions to be performed by the algorithm on a dataset.
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) override
Create a Child Algorithm.
Kernel::IPropertyManager::TypedValue getProperty(const std::string &name) const override
Get the property held by this object.
std::string getPropertyValue(const std::string &name) const override
Get the property held by this object.
Base MatrixWorkspace Abstract Class.
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
Class to hold a set of workspaces.
A property class for workspaces.
QENSFitSequential - Performs a sequential QENS fit.
std::vector< std::size_t > getDatasetGrouping(const std::vector< API::MatrixWorkspace_sptr > &workspaces) const
std::vector< API::MatrixWorkspace_sptr > convertInputToElasticQ(const std::vector< API::MatrixWorkspace_sptr > &workspaces) const
virtual std::map< std::string, std::string > getAdditionalLogNumbers() const
API::IAlgorithm_sptr extractMembersAlgorithm(const API::WorkspaceGroup_sptr &resultGroupWs, const std::string &outputWsName) const
void addFitRangeLogs(const API::Workspace_sptr &resultWorkspace, size_t itter)
void renameWorkspaces(API::WorkspaceGroup_sptr outputGroup, std::vector< std::string > const &spectra, std::string const &outputBaseName, std::string const &endOfSuffix)
std::string getInputString(const std::vector< API::MatrixWorkspace_sptr > &workspaces) const
virtual std::vector< std::string > getFitParameterNames() const
virtual std::vector< API::MatrixWorkspace_sptr > getWorkspaces() const
void renameGroupWorkspace(std::string const &currentName, std::vector< std::string > const &spectra, std::string const &outputBaseName, std::string const &endOfSuffix)
void copyLogs(const API::WorkspaceGroup_sptr &resultWorkspaces, std::vector< API::MatrixWorkspace_sptr > const &workspaces)
virtual std::map< std::string, std::string > getAdditionalLogStrings() const
API::WorkspaceGroup_sptr processIndirectFitParameters(const API::ITableWorkspace_sptr &parameterWorkspace, const std::vector< std::size_t > &grouping)
virtual API::ITableWorkspace_sptr processParameterTable(API::ITableWorkspace_sptr parameterTable)
void deleteTemporaryWorkspaces(const std::string &outputBaseName)
const std::string name() const override
Algorithms name for identification.
API::ITableWorkspace_sptr performFit(const std::string &input, const std::string &output)
void addAdditionalLogs(const API::WorkspaceGroup_sptr &resultWorkspace)
void extractMembers(const API::WorkspaceGroup_sptr &resultGroupWs, const std::vector< API::MatrixWorkspace_sptr > &workspaces, const std::string &outputWsName)
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
Definition: ListValidator.h:29
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
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< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
void renameWorkspacesInQENSFit(Algorithm *qensFit, IAlgorithm_sptr renameAlgorithm, const WorkspaceGroup_sptr &outputGroup, std::string const &outputBaseName, std::string const &groupSuffix, std::function< std::string(std::size_t)> const &getNameSuffix)
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
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
void renameWorkspacesWith(const WorkspaceGroup_sptr &groupWorkspace, std::function< std::string(std::size_t)> const &getName, std::function< void(Workspace_sptr, const std::string &)> const &renamer)
void renameWorkspace(const IAlgorithm_sptr &renamer, const Workspace_sptr &workspace, const std::string &newName)
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition: Algorithm.h:61
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
bool containsMultipleData(const std::vector< MatrixWorkspace_sptr > &workspaces)
std::shared_ptr< CompositeFunction > CompositeFunction_sptr
shared pointer to the composite function base class
std::shared_ptr< IValidator > IValidator_sptr
A shared_ptr to an IValidator.
Definition: IValidator.h:26
STL namespace.
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
@ Output
An output workspace.
Definition: Property.h:54