21#include <boost/algorithm/string.hpp>
22#include <boost/algorithm/string/detail/classification.hpp>
23#include <boost/algorithm/string/split.hpp>
34template <
typename T> std::string joinVector(std::vector<T>
const &vec, std::string
const &delimiter =
", ") {
36 std::copy(vec.cbegin(), vec.cend(), std::ostream_iterator<T>(ss, delimiter.c_str()));
37 auto const str = ss.str();
38 return str.substr(0, str.size() - delimiter.size());
41std::string constructInputDictionaryEntry(std::string
const &workspaceName, std::size_t
const &workspaceIndex,
42 double const startX,
double const endX) {
47std::string constructInputDictionary(std::vector<std::string>
const &inputWorkspaces,
48 std::vector<std::size_t>
const &workspaceIndices,
49 std::vector<double>
const &startXs, std::vector<double>
const &endXs) {
50 std::vector<std::string> entries;
51 entries.reserve(inputWorkspaces.size());
52 for (
auto i = 0u; i < inputWorkspaces.size(); ++i)
53 entries.emplace_back(constructInputDictionaryEntry(inputWorkspaces[i], workspaceIndices[i], startXs[i], endXs[i]));
55 return "{\n " + joinVector(entries,
",\n ") +
"\n}";
58std::vector<std::string>
splitStringBy(std::string
const &str, std::string
const &delimiter) {
59 std::vector<std::string> subStrings;
60 boost::split(subStrings, str, boost::is_any_of(delimiter));
61 subStrings.erase(std::remove_if(subStrings.begin(), subStrings.end(),
62 [](std::string
const &subString) { return subString.empty(); }),
67void replaceAll(std::string &str, std::string
const &remove, std::string
const &insert) {
68 std::string::size_type pos = 0;
69 while ((pos = str.find(remove, pos)) != std::string::npos) {
70 str.replace(pos, remove.size(), insert);
75std::string getFileContents(std::string
const &filename) {
78 std::ifstream filestream(directory +
"/" + filename);
81 throw std::runtime_error(
"Error occured when attempting to load file: " + filename);
84 std::string fileText((std::istreambuf_iterator<char>(filestream)), std::istreambuf_iterator<char>());
102 return "An algorithm to generate a Python script file for performing a sequential or simultaneous fit.";
108 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
109 mustBePositive->setLower(0);
113 "A list of workspace names to be fitted. The workspace name at index i in the list corresponds with "
114 "the 'WorkspaceIndices', 'StartXs' and 'EndXs' properties.");
116 "A list of workspace indices to be fitted. The workspace index at index i in the list will "
117 "correspond to the input workspace at index i.");
120 "A list of start X's to be used for the fitting. The Start X at index i will correspond to the input "
121 "workspace at index i.");
124 "A list of end X's to be used for the fitting. The End X at index i will correspond to the input "
125 "workspace at index i.");
127 auto const fittingTypes = std::vector<std::string>{
"Sequential",
"Simultaneous"};
128 auto const fittingTypesValidator = std::make_shared<ListValidator<std::string>>(fittingTypes);
130 "The type of fitting to generate a python script for (Sequential or Simultaneous).",
135 "The function to use for the fitting. This should be a single domain function if the Python script will be for "
136 "sequential fitting, or a MultiDomainFunction if the Python script is for simultaneous fitting.");
139 "The MaxIterations to be passed to the Fit algorithm in the Python script.",
Direction::Input);
142 auto const minimizerValidator = std::make_shared<ListValidator<std::string>>(minimizerOptions);
143 declareProperty(
"Minimizer",
"Levenberg-Marquardt", minimizerValidator,
144 "The Minimizer to be passed to the Fit algorithm in the Python script.",
Direction::Input);
147 auto const costFunctionValidator = std::make_shared<ListValidator<std::string>>(costFunctionOptions);
148 declareProperty(
"CostFunction",
"Least squares", costFunctionValidator,
149 "The CostFunction to be passed to the Fit algorithm in the Python script.",
Direction::Input);
151 std::array<std::string, 2> evaluationTypes = {{
"CentrePoint",
"Histogram"}};
153 "The EvaluationType to be passed to the Fit algorithm in the Python script.",
Direction::Input);
156 "The OutputBaseName is the base output name to use for the resulting Fit workspaces.");
160 "If true, code used for plotting the results of a fit will be generated and added to the python script.");
162 std::vector<std::string> extensions{
".py"};
164 "The name of the Python fit script which will be generated and saved in the selected location.");
170 std::vector<std::string>
const inputWorkspaces =
getProperty(
"InputWorkspaces");
171 std::vector<std::size_t>
const workspaceIndices =
getProperty(
"WorkspaceIndices");
172 std::vector<double>
const startXs =
getProperty(
"StartXs");
173 std::vector<double>
const endXs =
getProperty(
"EndXs");
176 std::string
const outputBaseName =
getProperty(
"OutputBaseName");
178 std::map<std::string, std::string> errors;
179 if (workspaceIndices.size() != inputWorkspaces.size())
180 errors[
"WorkspaceIndices"] =
"The number of workspace indices must be equal to the number of input workspaces.";
181 if (startXs.size() != inputWorkspaces.size())
182 errors[
"StartXs"] =
"The number of Start Xs must be equal to the number of input workspaces.";
183 if (endXs.size() != inputWorkspaces.size())
184 errors[
"EndXs"] =
"The number of End Xs must be equal to the number of input workspaces.";
185 if (fittingType ==
"Sequential") {
186 if (std::dynamic_pointer_cast<MultiDomainFunction>(function))
187 errors[
"Function"] =
"The Function cannot be a MultiDomainFunction when in Sequential fit mode.";
189 if (fittingType ==
"Simultaneous") {
191 errors[
"Function"] =
"The Function provided does not have the same number of domains as there are input "
192 "workspaces. This is a requirement for Simultaneous fitting.";
194 if (outputBaseName.empty())
195 errors[
"OutputBaseName"] =
"The OutputBaseName is empty, please provide a base name for the output fit.";
205 if (!filepath.empty())
214 if (
auto const multiDomainFunction = std::dynamic_pointer_cast<MultiDomainFunction>(function))
215 return multiDomainFunction->getNumberDomains();
220 std::string generatedScript;
222 generatedScript +=
"\n";
223 if (fittingType ==
"Sequential")
224 generatedScript += getFileContents(
"GeneratePythonFitScript_SequentialFit.py.in");
225 else if (fittingType ==
"Simultaneous")
230 generatedScript +=
"\n";
231 std::vector<double>
const startXs =
getProperty(
"StartXs");
232 if (startXs.size() == 1u) {
233 generatedScript += getFileContents(
"GeneratePythonFitScript_PlottingSingleOutput.py.in");
235 generatedScript += getFileContents(
"GeneratePythonFitScript_PlottingMultiOutput.py.in");
239 return generatedScript;
243 std::string code = getFileContents(
"GeneratePythonFitScript_VariableSetup.py.in");
245 std::vector<std::string>
const inputWorkspaces =
getProperty(
"InputWorkspaces");
246 std::vector<std::size_t>
const workspaceIndices =
getProperty(
"WorkspaceIndices");
247 std::vector<double>
const startXs =
getProperty(
"StartXs");
248 std::vector<double>
const endXs =
getProperty(
"EndXs");
250 int const maxIterations =
getProperty(
"MaxIterations");
251 std::string
const minimizer =
getProperty(
"Minimizer");
252 std::string
const costFunction =
getProperty(
"CostFunction");
253 std::string
const evaluationType =
getProperty(
"EvaluationType");
254 std::string
const outputBaseName =
getProperty(
"OutputBaseName");
256 replaceAll(code,
"{{input_dictionary}}", constructInputDictionary(inputWorkspaces, workspaceIndices, startXs, endXs));
258 replaceAll(code,
"{{max_iterations}}",
std::to_string(maxIterations));
259 replaceAll(code,
"{{minimizer}}", minimizer);
260 replaceAll(code,
"{{cost_function}}", costFunction);
261 replaceAll(code,
"{{evaluation_type}}", evaluationType);
262 replaceAll(code,
"{{output_base_name}}", outputBaseName);
267 std::string code = getFileContents(
"GeneratePythonFitScript_SimultaneousFit.py.in");
268 std::string
const line = getFileContents(
"GeneratePythonFitScript_SimultaneousFitDomainLine.py.in");
270 std::vector<std::string>
const inputWorkspaces =
getProperty(
"InputWorkspaces");
271 std::string domainLines;
272 for (
auto i = 1u; i < inputWorkspaces.size(); ++i) {
273 std::string snippet = line;
275 domainLines += snippet;
278 replaceAll(code,
"{{other_domains}}", domainLines);
284 auto const functionSplit = splitStringBy(function->asString(),
";");
286 std::string code =
"\\\n \"";
287 code += joinVector(functionSplit,
";\" \\\n \"");
293 std::ofstream file(filepath.c_str(), std::ofstream::trunc);
#define DECLARE_ALGORITHM(classname)
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
@ OptionalSave
to specify a file to write to but an empty string is
std::vector< std::string > const seeAlso() const override
Function to return all of the seeAlso algorithms related to this algorithm.
std::string generateFitScript(std::string const &fittingType) const
void init() override
Virtual method - must be overridden by concrete algorithm.
std::string generateFunctionString() const
std::string const category() const override
function to return a category of the algorithm.
std::string generateVariableSetupCode() const
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
std::string const summary() const override
function returns a summary message that will be displayed in the default GUI, and in the help.
std::string generateSimultaneousFitCode() const
void savePythonScript(std::string const &filepath, std::string const &contents) const
std::size_t getNumberOfDomainsInFunction(Mantid::API::IFunction_sptr const &function) const
int version() const override
function to return a version of the algorithm, must be overridden in all algorithms
void exec() override
Virtual method - must be overridden by concrete algorithm.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
std::shared_ptr< const IFunction > IFunction_const_sptr
shared pointer to the function base class (const version)
MANTID_KERNEL_DLL std::string replaceAll(const std::string &input, const std::string &charStr, const std::string &substitute)
Return a string with all occurrences of the characters in the input replaced by the replace string.
std::shared_ptr< IValidator > IValidator_sptr
A shared_ptr to an IValidator.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
@ Output
An output workspace.