Newer
Older
#include "MantidQtCustomInterfaces/ReflGenerateNotebook.h"
#include "MantidAPI/NotebookWriter.h"
#include "MantidQtCustomInterfaces/ParseKeyValueString.h"
#include <sstream>
#include <fstream>
#include <memory>
#include <boost/tokenizer.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
namespace MantidQt {
namespace CustomInterfaces {
ReflGenerateNotebook::ReflGenerateNotebook(std::string name, QReflTableModel_sptr model,
const std::string instrument, const int runs_column,
const int transmission_column, const int options_column,
const int angle_column, const int min_q, const int max_q,
const int d_qq, const int scale_column) :
m_wsName(name), m_model(model), m_instrument(instrument),
COL_RUNS(runs_column), COL_TRANSMISSION(transmission_column),
COL_OPTIONS(options_column), COL_ANGLE(angle_column),
COL_QMIN(min_q), COL_QMAX(max_q), COL_DQQ(d_qq), COL_SCALE(scale_column){ }
/**
Generate an ipython notebook
@param rows : rows in the model which were processed
@param groups : groups of rows which were stitched
@param filename : location to save notebook to
void ReflGenerateNotebook::generateNotebook(std::map<int, std::set<int>> groups, std::set<int> rows, std::string filename) {
std::unique_ptr<Mantid::API::NotebookWriter> notebook(new Mantid::API::NotebookWriter());
std::string title_string;
if (!m_wsName.empty()) {
title_string = "Processed data from workspace: " + m_wsName + "\n---------------------";
}
else {
title_string = "Processed data\n---------------------";
}
title_string += "\nNotebook generated from the ISIS Reflectometry (Polref) Interface";
notebook->markdownCell(title_string);
int groupNo = 1;
for (auto gIt = groups.begin(); gIt != groups.end(); ++gIt, ++groupNo) {
const std::set<int> groupRows = gIt->second;
// Announce the stitch group in the notebook
notebook->markdownCell(
"Stitch group " + static_cast<std::ostringstream*>( &(std::ostringstream() << groupNo) )->str());
//Reduce each row
std::ostringstream code_string;
std::tuple<std::string, std::string> reduce_row_string;
std::vector<std::string> ws_names;
code_string << "#Load and reduce\n";
for (auto rIt = groupRows.begin(); rIt != groupRows.end(); ++rIt) {
reduce_row_string = reduceRowString(*rIt);
code_string << std::get<0>(reduce_row_string);
ws_names.push_back(std::get<1>(reduce_row_string));
}
notebook->codeCell(code_string.str());
// Stitch group
std::tuple<std::string, std::string> stitch_string = stitchGroupString(groupRows);
notebook->codeCell(std::get<0>(stitch_string));
// Plot the unstitched and stitched I vs Q
std::ostringstream plot_string;
plot_string << "f, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(12,4))\n";
std::vector<std::string> stitched_ws;
stitched_ws.push_back(std::get<1>(stitch_string));
plot_string << plotIvsQ(ws_names, "ax1"); // unstitched
plot_string << plotIvsQ(stitched_ws, "ax2"); // stitched
plot_string << "plt.show() #Draw the plot\n";
notebook->codeCell(plot_string.str());
std::string generatedNotebook = notebook->writeNotebook();
std::ofstream file(filename.c_str(), std::ofstream::trunc);
file << generatedNotebook;
file.flush();
file.close();
}
/**
Create string of python code to stitch workspaces in the same group
@param rows : rows in the stitch group
@return tuple containing the python code string and the output workspace name
*/
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
std::tuple<std::string, std::string> ReflGenerateNotebook::stitchGroupString(std::set<int> rows)
{
std::ostringstream stitch_string;
stitch_string << "#Stitch workspaces\n";
//If we can get away with doing nothing, do.
if(rows.size() < 2)
return std::make_tuple("", "");
//Properties for Stitch1DMany
std::vector<std::string> workspaceNames;
std::vector<std::string> runs;
std::vector<double> params;
std::vector<double> startOverlaps;
std::vector<double> endOverlaps;
//Go through each row and prepare the properties
for(auto rowIt = rows.begin(); rowIt != rows.end(); ++rowIt)
{
const std::string runStr = m_model->data(m_model->index(*rowIt, COL_RUNS)).toString().toStdString();
const double qmin = m_model->data(m_model->index(*rowIt, COL_QMIN)).toDouble();
const double qmax = m_model->data(m_model->index(*rowIt, COL_QMAX)).toDouble();
const std::tuple<std::string, std::string> load_ws_string = loadWorkspaceString(runStr);
const std::string runNo = getRunNumber(std::get<1>(load_ws_string));
runs.push_back(runNo);
workspaceNames.push_back("IvsQ_" + runNo);
startOverlaps.push_back(qmin);
endOverlaps.push_back(qmax);
}
double dqq = m_model->data(m_model->index(*(rows.begin()), COL_DQQ)).toDouble();
//params are qmin, -dqq, qmax for the final output
params.push_back(*std::min_element(startOverlaps.begin(), startOverlaps.end()));
params.push_back(-dqq);
params.push_back(*std::max_element(endOverlaps.begin(), endOverlaps.end()));
//startOverlaps and endOverlaps need to be slightly offset from each other
//See usage examples of Stitch1DMany to see why we discard first qmin and last qmax
startOverlaps.erase(startOverlaps.begin());
endOverlaps.pop_back();
std::string outputWSName = "IvsQ_" + boost::algorithm::join(runs, "_");
stitch_string << outputWSName << ", _ = Stitch1DMany(";
stitch_string << vectorParamString("InputWorkspaces", workspaceNames);
stitch_string << ", ";
stitch_string << vectorParamString("Params", params);
stitch_string << ", ";
stitch_string << vectorParamString("StartOverlaps", startOverlaps);
stitch_string << ", ";
stitch_string << vectorParamString("EndOverlaps", endOverlaps);
stitch_string << ")\n";
return std::make_tuple(stitch_string.str(), outputWSName);
}
/**
Create string of comma separated list of parameter values from a vector
@param param_name : name of the parameter we are creating a list of
@param param_vec : vector of parameter values
@return string of comma separated list of parameter values
*/
template<typename T, typename A>
std::string ReflGenerateNotebook::vectorParamString(std::string param_name, std::vector<T,A> ¶m_vec)
{
std::ostringstream vector_string;
const char* separator = "";
vector_string << param_name << " = '";
for(auto paramIt = param_vec.begin(); paramIt != param_vec.end(); ++paramIt)
{
vector_string << separator << *paramIt;
separator = ", ";
}
vector_string << "'";
return vector_string.str();
}
/**
Create string of python code to plot I vs Q from workspaces
@param ws_names : vector of workspace names to plot on the same axes
@param axes : handle of axes to plot in
@return string of python code to plot I vs Q
*/
std::string ReflGenerateNotebook::plotIvsQ(std::vector<std::string> ws_names, std::string axes) {
std::ostringstream plot_string;
plot_string << "#Plot I vs Q\n";
for (auto it = ws_names.begin(); it != ws_names.end(); ++it) {
std::tuple<std::string, std::string> convert_point_string = convertToPointString(*it);
plot_string << std::get<0>(convert_point_string);
plot_string << axes << ".loglog(" << std::get<1>(convert_point_string) << ".readX(0), "
<< std::get<1>(convert_point_string) << ".readY(0), "
<< "basex=10, label='" << *it << "')\n";
}
plot_string << axes << ".set_title('I vs Q')\n";
plot_string << axes << ".grid() #Show a grid\n";
plot_string << axes << ".legend() #Show a legend\n";
return plot_string.str();
}
Create string of python code to run reduction algorithm on the specified row
@param rowNo : the row in the model to run the reduction algorithm on
@return tuple containing the python string and the output workspace name
std::tuple<std::string, std::string> ReflGenerateNotebook::reduceRowString(int rowNo) {
std::ostringstream code_string;
const std::string runStr = m_model->data(m_model->index(rowNo, COL_RUNS)).toString().toStdString();
const std::string transStr = m_model->data(m_model->index(rowNo, COL_TRANSMISSION)).toString().toStdString();
const std::string options = m_model->data(m_model->index(rowNo, COL_OPTIONS)).toString().toStdString();
double theta = 0;
const bool thetaGiven = !m_model->data(m_model->index(rowNo, COL_ANGLE)).toString().isEmpty();
if (thetaGiven)
theta = m_model->data(m_model->index(rowNo, COL_ANGLE)).toDouble();
const std::tuple<std::string, std::string> load_ws_string = loadWorkspaceString(runStr);
code_string << std::get<0>(load_ws_string);
const std::string runNo = getRunNumber(std::get<1>(load_ws_string));
if (!transStr.empty()) {
const std::tuple<std::string, std::string> trans_string = transWSString(transStr);
code_string << std::get<0>(trans_string);
code_string << "IvsQ_" << runNo << ", " << "IvsLam_" << runNo << ", _ = ";
code_string << "ReflectometryReductionOneAuto(InputWorkspace = '" << std::get<1>(load_ws_string) << "'";
code_string << ", " << "FirstTransmissionRun = '" << std::get<1>(trans_string) << "'";
}
else {
code_string << "IvsQ_" << runNo << ", " << "IvsLam_" << runNo << ", _ = ";
code_string << "ReflectometryReductionOneAuto(InputWorkspace = '" << std::get<1>(load_ws_string) << "'";
}
if (thetaGiven)
code_string << ", " << "ThetaIn = " << theta;
//Parse and set any user-specified options
auto optionsMap = parseKeyValueString(options);
for (auto kvp = optionsMap.begin(); kvp != optionsMap.end(); ++kvp) {
code_string << ", " << kvp->first << " = " << kvp->second;
}
code_string << ")\n";
const double scale = m_model->data(m_model->index(rowNo, COL_SCALE)).toDouble();
if(scale != 1.0) {
const std::tuple<std::string, std::string> scale_string = scaleString(runNo, scale);
code_string << std::get<0>(scale_string);
}
const std::tuple<std::string, std::string> rebin_string = rebinString(rowNo, runNo);
code_string << std::get<0>(rebin_string);
return std::make_tuple(code_string.str(), std::get<1>(rebin_string));
/**
Create string of python code to run the scale algorithm
@param runNo : the number of the run to scale
@param scale : value of scaling factor to use
@return tuple of strings of python code and output workspace name
*/
std::tuple<std::string, std::string> ReflGenerateNotebook::scaleString(std::string runNo, double scale)
{
std::ostringstream scale_string;
scale_string << "IvsQ_" << runNo << " = Scale(";
scale_string << "InputWorkspace = IvsQ_" << runNo;
scale_string << ", Factor = " << 1.0 / scale;
scale_string << ")\n";
return std::make_tuple(scale_string.str(), "IvsQ" + runNo);
}
/**
Create string of python code to convert to point data, which can be plotted
@param wsName : name of workspace to convert to point data
@return tuple of strings of python code and output workspace name
*/
std::tuple<std::string, std::string> ReflGenerateNotebook::convertToPointString(std::string wsName)
{
const std::string output_name = wsName + "_plot";
std::ostringstream convert_string;
convert_string << output_name << " = ConvertToPointData(" << wsName << ")\n";
return std::make_tuple(convert_string.str(), output_name);
/**
Create string of python code to rebin data in a workspace
@param rowNo : the number of the row to rebin
@param runNo : the number of the run to rebin
@return tuple of strings of python code and output workspace name
*/
std::tuple<std::string, std::string> ReflGenerateNotebook::rebinString(int rowNo, std::string runNo)
{
//We need to make sure that qmin and qmax are respected, so we rebin to
//those limits here.
std::ostringstream rebin_string;
rebin_string << "IvsQ_" << runNo << " = ";
rebin_string << "Rebin(";
rebin_string << "IvsQ_" << runNo;
const double qmin = m_model->data(m_model->index(rowNo, COL_QMIN)).toDouble();
const double qmax = m_model->data(m_model->index(rowNo, COL_QMAX)).toDouble();
const double dqq = m_model->data(m_model->index(rowNo, COL_DQQ)).toDouble();
rebin_string << ", " << "Params = ";
rebin_string << "'" << qmin << ", " << -dqq << ", " << qmax << "'";
rebin_string << ")\n";
return std::make_tuple(rebin_string.str(), "IvsQ_" + runNo);
/**
Create string of python code to create a transmission workspace
@param trans_ws_str : string of workspaces to create transmission workspace from
@return tuple of strings of python code and output workspace name
*/
std::tuple<std::string, std::string> ReflGenerateNotebook::transWSString(std::string trans_ws_str)
{
const size_t maxTransWS = 2;
std::vector<std::string> transVec;
std::ostringstream trans_string;
std::vector<std::string> trans_ws_name;
//Take the first two run numbers
boost::split(transVec, trans_ws_str, boost::is_any_of(","));
if(transVec.size() > maxTransWS)
transVec.resize(maxTransWS);
std::tuple<std::string, std::string> load_tuple;
for(auto it = transVec.begin(); it != transVec.end(); ++it)
load_tuple = loadWorkspaceString(*it);
trans_ws_name.push_back(std::get<1>(load_tuple));
trans_string << std::get<0>(load_tuple);
//The runs are loaded, so we can create a TransWS
std::string wsName = "TRANS_" + getRunNumber(trans_ws_name[0]);
if(trans_ws_name.size() > 1)
wsName += "_" + getRunNumber(trans_ws_name[1]);
trans_string << wsName << " = ";
trans_string << "CreateTransmissionWorkspaceAuto(";
trans_string << "FirstTransmissionRun = '" << trans_ws_name[0] << "'";
if(trans_ws_name.size() > 1)
trans_string << ", SecondTransmissionRun = '" << trans_ws_name[1] << "'";
return std::make_tuple(trans_string.str(), wsName);
/**
Get run number from workspace name
@param ws_name : workspace name
@return run number, as a string
*/
std::string ReflGenerateNotebook::getRunNumber(std::string ws_name) {
//Matches TOF_13460 -> 13460
boost::regex outputRegex("(TOF|IvsQ|IvsLam)_([0-9]+)");
//Matches INTER13460 -> 13460
boost::regex instrumentRegex("[a-zA-Z]{3,}([0-9]{3,})");
boost::smatch matches;
if (boost::regex_match(ws_name, matches, outputRegex)) {
return matches[2].str();
}
else if (boost::regex_match(ws_name, matches, instrumentRegex)) {
return matches[1].str();
}
//Resort to using the workspace name
return ws_name;
}
/**
Create string of python code to load workspaces
@param runStr : string of workspaces to load
@return tuple of strings of python code and output workspace name
*/
std::tuple<std::string, std::string> ReflGenerateNotebook::loadWorkspaceString(std::string runStr) {
std::vector<std::string> runs;
boost::split(runs, runStr, boost::is_any_of("+"));
std::ostringstream load_strings;
//Remove leading/trailing whitespace from each run
for (auto runIt = runs.begin(); runIt != runs.end(); ++runIt)
boost::trim(*runIt);
const std::string outputName = "TOF_" + boost::algorithm::join(runs, "_");
std::tuple<std::string, std::string> load_string;
load_string = loadRunString(runs[0]);
load_strings << std::get<0>(load_string);
// EXIT POINT if there is only one run
if(runs.size() == 1) {
return std::make_tuple(load_strings.str(), std::get<1>(load_string));
}
load_strings << outputName << " = " << std::get<1>(load_string) << "\n";
// Load each subsequent run and add it to the first run
for(auto runIt = std::next(runs.begin()); runIt != runs.end(); ++runIt)
{
load_string = loadRunString(*runIt);
load_strings << std::get<0>(load_string);
load_strings << plusString(std::get<1>(load_string), outputName);
}
return std::make_tuple(load_strings.str(), outputName);
}
/**
Create string of python code to run the Plus algorithm on specified workspaces
@param input_name : name of workspace to add to the other workspace
@param output_name : other workspace will be added to the one with this name
@return string of python code
*/
std::string ReflGenerateNotebook::plusString(std::string input_name, std::string output_name)
{
std::ostringstream plus_string;
plus_string << output_name << " = Plus('LHSWorkspace' = " << output_name;
plus_string << ", 'RHSWorkspace' = " << input_name;
plus_string << ")\n";
return plus_string.str();
/**
Create string of python code to load a single workspace
@param run : run to load
@return tuple of strings of python code and output workspace name
*/
std::tuple<std::string, std::string> ReflGenerateNotebook::loadRunString(std::string run) {
std::ostringstream load_string;
// We do not have access to AnalysisDataService from notebook, so must load run from file
const std::string filename = m_instrument + run;
const std::string ws_name = "TOF_" + run;
load_string << ws_name << " = ";
load_string << "Load(";
load_string << "Filename = '" << filename << "'";
load_string << ")\n";
return std::make_tuple(load_string.str(), ws_name);