"git@code.ornl.gov:mantidproject/mantid.git" did not exist on "f4758afcfdb62898adbcb0e5f3cfc4324f81e027"
Newer
Older
#include "MantidQtCustomInterfaces/Reflectometry/ReflDataProcessorPresenter.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/IEventWorkspace.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/Run.h"
#include "MantidQtMantidWidgets/DataProcessorUI/DataProcessorTreeManager.h"
#include "MantidQtMantidWidgets/DataProcessorUI/DataProcessorView.h"
#include "MantidQtMantidWidgets/DataProcessorUI/ParseKeyValueString.h"
#include "MantidQtMantidWidgets/ProgressPresenter.h"
using namespace MantidQt::MantidWidgets;
using namespace Mantid::API;
namespace MantidQt {
namespace CustomInterfaces {
/**
* Constructor
* @param whitelist : The set of properties we want to show as columns
* @param preprocessMap : A map containing instructions for pre-processing
* @param processor : A DataProcessorProcessingAlgorithm
* @param postprocessor : A DataProcessorPostprocessingAlgorithm
* workspaces
* @param postprocessMap : A map containing instructions for post-processing.
* This map links column name to properties of the post-processing algorithm
* @param loader : The algorithm responsible for loading data
*/
ReflDataProcessorPresenter::ReflDataProcessorPresenter(
const DataProcessorWhiteList &whitelist,
const std::map<std::string, DataProcessorPreprocessingAlgorithm> &
preprocessMap,
const DataProcessorProcessingAlgorithm &processor,
const DataProcessorPostprocessingAlgorithm &postprocessor,
const std::map<std::string, std::string> &postprocessMap,
const std::string &loader)
: GenericDataProcessorPresenter(whitelist, preprocessMap, processor,
postprocessor, postprocessMap, loader) {}
/**
* Destructor
*/
ReflDataProcessorPresenter::~ReflDataProcessorPresenter() {}
/**
Process selected data
*/
void ReflDataProcessorPresenter::process() {
// If uniform slicing is empty process normally, delegating to
// GenericDataProcessorPresenter
std::string timeSlicingValues = m_mainPresenter->getTimeSlicingValues();
if (timeSlicingValues.empty()) {
GenericDataProcessorPresenter::process();
return;
}
// Get time slicing type
std::string timeSlicingType = m_mainPresenter->getTimeSlicingType();
// Get selected runs
const auto items = m_manager->selectedData(true);
// Progress report
int progress = 0;
int maxProgress = (int)(items.size());
ProgressPresenter progressReporter(progress, maxProgress, maxProgress,
m_progressView);
// True if all groups were processed as event workspaces
bool allGroupsWereEvent = true;
// True if errors where encountered when reducing table
bool errors = false;
for (const auto &item : items) {
// Group of runs
GroupData group = item.second;
try {
// First load the runs.
bool allEventWS = loadGroup(group);
if (allEventWS) {
// Process the group
if (processGroupAsEventWS(item.first, group, timeSlicingType,
timeSlicingValues))
errors = true;
// Notebook not implemented yet
if (m_view->getEnableNotebook()) {
GenericDataProcessorPresenter::giveUserWarning(
"Notebook not implemented for sliced data yet",
"Notebook will not be generated");
}
} else {
// Process the group
if (processGroupAsNonEventWS(item.first, group))
errors = true;
// Notebook
if (!allEventWS)
allGroupsWereEvent = false;
} catch (...) {
errors = true;
progressReporter.report();
}
if (!allGroupsWereEvent)
m_mainPresenter->giveUserWarning(
"Some groups could not be processed as event workspaces", "Warning");
if (errors)
m_mainPresenter->giveUserWarning("Some errors were encountered when "
"reducing table. Some groups may not have "
"been fully processed.",
"Warning");
progressReporter.clear();
}
/** Loads a group of runs. Tries loading runs as event workspaces. If any of the
* workspaces in the group is not an event workspace, stops loading and re-loads
* all of them as non-event workspaces. We need the workspaces to be of the same
* type to process them together.
*
* @param group :: the group of runs
* @return :: true if all runs were loaded as event workspaces. False otherwise
*/
bool ReflDataProcessorPresenter::loadGroup(const GroupData &group) {
// Set of runs loaded successfully
std::set<std::string> loadedRuns;
for (const auto &row : group) {
// The run number
std::string runNo = row.second.at(0);
// Try loading as event workspace
if (!eventWS) {
// This run could not be loaded as event workspace. We need to load and
// process the whole group as non-event data.
for (const auto &rowNew : group) {
std::string runNo = rowNew.second.at(0);
// Load as non-event workspace
// Remove monitors which were loaded as separate workspaces
for (const auto &run : loadedRuns) {
AnalysisDataService::Instance().remove("TOF_" + run + "_monitors");
}
return false;
return true;
}
/** Processes a group of runs
*
* @param groupID :: An integer number indicating the id of this group
* @param group :: the group of event workspaces
* @param timeSlicingType :: The type of time slicing being used
* @param timeSlicingValues :: The string of values to perform time slicing with
* @return :: true if errors were encountered
bool ReflDataProcessorPresenter::processGroupAsEventWS(
int groupID, const GroupData &group, const std::string &timeSlicingType,
const std::string &timeSlicingValues) {
bool errors = false;
bool multiRow = group.size() > 1;
size_t numGroupSlices = INT_MAX;
const std::string filterAlg =
timeSlicingType == "LogValue" ? "FilterByLogValue" : "FilterByTime";
std::vector<double> startTimes, stopTimes;
std::string logFilter; // Set if we are slicing by log value
// For custom/log value slicing the start/stop times are the same for all rows
if (timeSlicingType == "Custom")
parseCustom(timeSlicingValues, startTimes, stopTimes);
if (timeSlicingType == "LogValue")
parseLogValue(timeSlicingValues, logFilter, startTimes, stopTimes);
for (const auto &row : group) {
const auto rowID = row.first; // Integer ID of this row
const auto data = row.second; // Vector containing data for this row
std::string runNo = row.second.at(0); // The run number
if (timeSlicingType == "UniformEven" || timeSlicingType == "Uniform") {
const std::string runName = "TOF_" + runNo;
parseUniform(timeSlicingValues, timeSlicingType, runName, startTimes,
stopTimes);
size_t numSlices = startTimes.size();
addNumSlicesEntry(groupID, rowID, numSlices);
for (size_t i = 0; i < numSlices; i++) {
try {
std::vector<std::string> slice(data);
std::string wsName =
takeSlice(runNo, i, startTimes[i], stopTimes[i], logFilter);
slice[0] = wsName;
auto newData = reduceRow(slice);
newData[0] = data[0];
m_manager->update(groupID, rowID, newData);
} catch (...) {
return true;
}
// For uniform slicing with multiple rows only the minimum number of slices
// are common to each row
if (multiRow && timeSlicingType == "Uniform")
numGroupSlices = std::min(numGroupSlices, numSlices);
// Post-process (if needed)
// All slices are common for uniform even, custom and log value slicing
if (timeSlicingType != "Uniform")
numGroupSlices = startTimes.size();
addNumGroupSlicesEntry(groupID, numGroupSlices);
for (size_t i = 0; i < numGroupSlices; i++) {
GroupData groupNew;
std::vector<std::string> data;
for (const auto &row : group) {
data = row.second;
data[0] = row.second[0] + "_slice_" + std::to_string(i);
groupNew[row.first] = data;
postProcessGroup(groupNew);
} catch (...) {
errors = true;
return errors;
}
/** Processes a group of non-event workspaces
*
* @param groupID :: An integer number indicating the id of this group
* @param group :: the group of event workspaces
* @return :: true if errors were encountered
*/
bool ReflDataProcessorPresenter::processGroupAsNonEventWS(
int groupID, const GroupData &group) {
bool errors = false;
for (const auto &row : group) {
// Reduce this row
auto newData = reduceRow(row.second);
// Update the tree
m_manager->update(groupID, row.first, newData);
}
// Post-process (if needed)
if (group.size() > 1) {
try {
postProcessGroup(group);
} catch (...) {
errors = true;
}
}
return errors;
/** Parses a string to extract uniform time slicing
*
* @param timeSlicing :: The string to parse
* @param slicingType :: The type of uniform slicing being used
* @param wsName :: The name of the workspace to be sliced
* @param startTimes :: Start times for the set of slices
* @param stopTimes :: Stop times for the set of slices
*/
void ReflDataProcessorPresenter::parseUniform(const std::string &timeSlicing,
const std::string &slicingType,
const std::string &wsName,
std::vector<double> &startTimes,
std::vector<double> &stopTimes) {
IEventWorkspace_sptr mws;
if (AnalysisDataService::Instance().doesExist(wsName)) {
mws = AnalysisDataService::Instance().retrieveWS<IEventWorkspace>(wsName);
if (!mws) {
m_mainPresenter->giveUserCritical("Workspace to slice " + wsName +
" is not an event workspace!",
"Time slicing error");
return;
}
} else {
m_mainPresenter->giveUserCritical("Workspace to slice not found: " + wsName,
"Time slicing error");
return;
}
const auto run = mws->run();
const auto totalDuration = run.endTime() - run.startTime();
double totalDurationSec = totalDuration.total_seconds();
if (slicingType == "UniformEven") {
sliceDuration = totalDurationSec / numSlices;
sliceDuration = std::stod(timeSlicing);
numSlices = static_cast<int>(ceil(totalDurationSec / sliceDuration));
// Add the start/stop times
startTimes = std::vector<double>(numSlices);
stopTimes = std::vector<double>(numSlices);
startTimes[i] = sliceDuration * i;
stopTimes[i] = sliceDuration * (i + 1);
}
}
/** Parses a string to extract custom time slicing
*
* @param timeSlicing :: The string to parse
* @param startTimes :: Start times for the set of slices
* @param stopTimes :: Stop times for the set of slices
*/
void ReflDataProcessorPresenter::parseCustom(const std::string &timeSlicing,
std::vector<double> &startTimes,
std::vector<double> &stopTimes) {
std::vector<std::string> timesStr;
boost::split(timesStr, timeSlicing, boost::is_any_of(","));
std::vector<double> times;
std::transform(timesStr.begin(), timesStr.end(), std::back_inserter(times),
[](const std::string &astr) { return std::stod(astr); });
size_t numTimes = times.size();
// Add the start/stop times
startTimes = std::vector<double>(numTimes - 1);
stopTimes = std::vector<double>(numTimes - 1);
startTimes[0] = 0;
stopTimes[0] = times[0];
for (size_t i = 0; i < numTimes - 1; i++) {
startTimes[i] = times[i];
stopTimes[i] = times[i + 1];
/** Parses a string to extract log value filter and time slicing
*
* @param inputStr :: The string to parse
* @param logFilter :: The log filter to use
* @param startTimes :: Start times for the set of slices
* @param stopTimes :: Stop times for the set of slices
*/
void ReflDataProcessorPresenter::parseLogValue(const std::string &inputStr,
std::string &logFilter,
std::vector<double> &startTimes,
std::vector<double> &stopTimes) {
auto strMap = parseKeyValueString(inputStr);
std::string timeSlicing = strMap.at("Slicing");
logFilter = strMap.at("LogFilter");
parseCustom(timeSlicing, startTimes, stopTimes);
}
/** Loads an event workspace and puts it into the ADS
*
* @param runNo :: The run number as a string
* @return :: True if algorithm was executed. False otherwise
bool ReflDataProcessorPresenter::loadEventRun(const std::string &runNo) {
std::string runName = "TOF_" + runNo;
IAlgorithm_sptr algLoadRun =
AlgorithmManager::Instance().create("LoadEventNexus");
algLoadRun->initialize();
algLoadRun->setProperty("Filename", m_view->getProcessInstrument() + runNo);
algLoadRun->setProperty("OutputWorkspace", runName);
algLoadRun->setProperty("LoadMonitors", true);
algLoadRun->execute();
return algLoadRun->isExecuted();
}
/** Loads a non-event workspace and puts it into the ADS
* @param runNo :: The run number as a string
void ReflDataProcessorPresenter::loadNonEventRun(const std::string &runNo) {
std::string runName = "TOF_" + runNo;
IAlgorithm_sptr algLoadRun =
AlgorithmManager::Instance().create("LoadISISNexus");
algLoadRun->setProperty("Filename", m_view->getProcessInstrument() + runNo);
algLoadRun->setProperty("OutputWorkspace", runName);
algLoadRun->execute();
}
/** Takes a slice from a run and puts the 'sliced' workspace into the ADS
*
* @param filterAlg :: The filter algorithm to use
* @param runNo :: The run number as a string
* @param sliceIndex :: The index of the slice being taken
* @param startTime :: Start time
* @param stopTime :: Stop time
* @param logFilter :: The log filter to use if slicing by log value
* @return :: the name of the sliced workspace (without prefix 'TOF_')
std::string ReflDataProcessorPresenter::takeSlice(const std::string &runNo,
double stopTime,
std::string logFilter) {
std::string runName = "TOF_" + runNo;
std::string sliceName = runName + "_slice_" + std::to_string(sliceIndex);
std::string monName = runName + "_monitors";
// Use FilterByTime if logFilter isn't set and FilterByLogValue otherwise
double scaleFactor;
if (logFilter.empty())
doFilterByTime(runName, sliceName, startTime, stopTime, scaleFactor);
else
doFilterByLogValue(runName, sliceName, startTime, stopTime, logFilter,
scaleFactor);
IAlgorithm_sptr scale = AlgorithmManager::Instance().create("Scale");
scale->initialize();
scale->setProperty("InputWorkspace", monName);
scale->setProperty("Factor", scaleFactor);
scale->setProperty("OutputWorkspace", "__" + monName + "_temp");
IAlgorithm_sptr rebinDet =
AlgorithmManager::Instance().create("RebinToWorkspace");
rebinDet->setProperty("WorkspaceToRebin", sliceName);
rebinDet->setProperty("WorkspaceToMatch", "__" + monName + "_temp");
rebinDet->setProperty("OutputWorkspace", sliceName);
rebinDet->setProperty("PreserveEvents", false);
rebinDet->execute();
IAlgorithm_sptr append = AlgorithmManager::Instance().create("AppendSpectra");
append->initialize();
append->setProperty("InputWorkspace1", "__" + monName + "_temp");
append->setProperty("InputWorkspace2", sliceName);
append->setProperty("OutputWorkspace", sliceName);
append->setProperty("MergeLogs", true);
append->execute();
// Remove temporary monitor ws
AnalysisDataService::Instance().remove("__" + monName + "_temp");
return sliceName.substr(4);
}
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
/** Performs FilterByTime on a run and obtains the slice normalization constant
*
* @param runName :: The name of the run being filtered
* @param sliceName :: The index of the slice being taken
* @param startTime :: Start time
* @param stopTime :: Stop time
* @param fraction :: The slice normalization constant
*/
void ReflDataProcessorPresenter::doFilterByTime(const std::string &runName,
const std::string &sliceName,
double startTime,
double stopTime,
double &fraction) {
// Filter the run
IAlgorithm_sptr filter = AlgorithmManager::Instance().create("FilterByTime");
filter->initialize();
filter->setProperty("InputWorkspace", runName);
filter->setProperty("OutputWorkspace", sliceName);
filter->setProperty("StartTime", startTime);
filter->setProperty("StopTime", stopTime);
filter->execute();
// Obtain the normalization constant for this slice
MatrixWorkspace_sptr mws =
AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(runName);
double total = mws->run().getProtonCharge();
mws = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(sliceName);
double slice = mws->run().getProtonCharge();
fraction = slice / total;
}
/** Performs FilterByLogValue on a run and obtains the slice normalization
*constant
*
* @param runName :: The name of the run being filtered
* @param sliceName :: The index of the slice being taken
* @param startTime :: Start time
* @param stopTime :: Stop time
* @param logFilter :: The name of the log filter
* @param fraction :: The slice normalization constant
*/
void ReflDataProcessorPresenter::doFilterByLogValue(
const std::string &runName, const std::string &sliceName, double minValue,
double maxValue, const std::string &logFilter, double &fraction) {
// Filter the run
IAlgorithm_sptr filter =
AlgorithmManager::Instance().create("FilterByLogValue");
filter->initialize();
filter->setProperty("InputWorkspace", runName);
filter->setProperty("OutputWorkspace", sliceName);
filter->setProperty("MinimumValue", minValue);
filter->setProperty("MaximumValue", maxValue);
filter->setProperty("LogName", logFilter);
filter->execute();
// Obtain the normalization constant for this slice
IEventWorkspace_sptr ews =
AnalysisDataService::Instance().retrieveWS<IEventWorkspace>(runName);
double total = ews->run().getProtonCharge();
ews = AnalysisDataService::Instance().retrieveWS<IEventWorkspace>(sliceName);
double slice = ews->run().getProtonCharge();
fraction = slice / total;
}
/** Plots any currently selected rows */
void ReflDataProcessorPresenter::plotRow() {
const auto items = m_manager->selectedData();
if (items.size() == 0)
return;
// If slicing values are empty plot normally
std::string timeSlicingValues = m_mainPresenter->getTimeSlicingValues();
if (timeSlicingValues.empty()) {
GenericDataProcessorPresenter::plotRow();
return;
}
// Set of workspaces to plot
std::set<std::string> workspaces;
// Set of workspaces not found in the ADS
std::set<std::string> notFound;
for (const auto &item : items) {
for (const auto &run : item.second) {
const size_t numSlices = m_numSlicesMap.at(item.first).at(run.first);
const std::string wsName = getReducedWorkspaceName(run.second, "IvsQ_");
for (size_t slice = 0; slice < numSlices; slice++) {
const std::string sliceName =
wsName + "_slice_" + std::to_string(slice);
if (AnalysisDataService::Instance().doesExist(sliceName))
workspaces.insert(sliceName);
else
notFound.insert(sliceName);
}
}
}
if (!notFound.empty())
m_mainPresenter->giveUserWarning(
"The following workspaces were not plotted because they were not "
"found:\n" +
boost::algorithm::join(notFound, "\n") +
"\n\nPlease check that the rows you are trying to plot have been "
"fully processed.",
"Error plotting rows.");
plotWorkspaces(workspaces);
}
/** This method returns, for a given set of rows, i.e. a group of runs, the name
* of the output (post-processed) workspace
*
* @param groupData : The data in a given group
* @param prefix : A prefix to be appended to the generated ws name
* @param index : The index of the slice
* @returns : The name of the workspace
*/
std::string ReflDataProcessorPresenter::getPostprocessedWorkspaceName(
const GroupData &groupData, const std::string &prefix, size_t index) {
std::vector<std::string> outputNames;
for (const auto &data : groupData) {
outputNames.push_back(getReducedWorkspaceName(data.second) + "_slice_" +
std::to_string(index));
}
return prefix + boost::join(outputNames, "_");
}
/** Plots any currently selected groups */
void ReflDataProcessorPresenter::plotGroup() {
const auto items = m_manager->selectedData();
if (items.size() == 0)
return;
// If slicing values are empty plot normally
std::string timeSlicingValues = m_mainPresenter->getTimeSlicingValues();
if (timeSlicingValues.empty()) {
GenericDataProcessorPresenter::plotGroup();
return;
}
// Set of workspaces to plot
std::set<std::string> workspaces;
// Set of workspaces not found in the ADS
std::set<std::string> notFound;
for (const auto &item : items) {
if (item.second.size() > 1) {
size_t numSlices = m_numGroupSlicesMap.at(item.first);
for (size_t slice = 0; slice < numSlices; slice++) {
const std::string wsName =
getPostprocessedWorkspaceName(item.second, "IvsQ_", slice);
if (AnalysisDataService::Instance().doesExist(wsName))
workspaces.insert(wsName);
else
notFound.insert(wsName);
}
}
}
if (!notFound.empty())
m_mainPresenter->giveUserWarning(
"The following workspaces were not plotted because they were not "
"found:\n" +
boost::algorithm::join(notFound, "\n") +
"\n\nPlease check that the groups you are trying to plot have been "
"fully processed.",
"Error plotting groups.");
plotWorkspaces(workspaces);
/** Add entry for the number of slices for a row in a group
*
* @param groupID :: The ID of the group
* @param rowID :: The ID of the row in group
* @param numSlices :: Number of slices
*/
void ReflDataProcessorPresenter::addNumSlicesEntry(int groupID, int rowID,
size_t numSlices) {
m_numSlicesMap[groupID][rowID] = numSlices;
}
/** Add entry for the number of slices for all rows in a group
*
* @param groupID :: The ID of the group
* @param numSlices :: Number of slices
*/
void ReflDataProcessorPresenter::addNumGroupSlicesEntry(int groupID,
size_t numSlices) {
m_numGroupSlicesMap[groupID] = numSlices;
}