Newer
Older
#include "MantidMDAlgorithms/MergeMD.h"
#include "MantidKernel/Strings.h"
#include "MantidGeometry/MDGeometry/IMDDimension.h"
#include "MantidDataObjects/MDEventFactory.h"
#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidDataObjects/MDBoxIterator.h"
#include "MantidKernel/CPUTimer.h"
#include "MantidKernel/MandatoryValidator.h"
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::Geometry;
using namespace Mantid::DataObjects;
namespace Mantid {
namespace MDAlgorithms {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(MergeMD)
//----------------------------------------------------------------------------------------------
/** Constructor
*/
MergeMD::MergeMD() {}
//----------------------------------------------------------------------------------------------
/** Destructor
*/
MergeMD::~MergeMD() {}
//----------------------------------------------------------------------------------------------
/// Algorithm's name for identification. @see Algorithm::name
const std::string MergeMD::name() const { return "MergeMD"; }
/// Algorithm's version for identification. @see Algorithm::version
int MergeMD::version() const { return 1; }
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/// Algorithm's category for identification. @see Algorithm::category
const std::string MergeMD::category() const { return "MDAlgorithms"; }
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void MergeMD::init() {
// declare arbitrary number of input m_workspaces as a list of strings at the
// moment
declareProperty(
new ArrayProperty<std::string>(
"InputWorkspaces",
boost::make_shared<MandatoryValidator<std::vector<std::string>>>()),
"The names of the input MDWorkspaces as a comma-separated list");
declareProperty(new WorkspaceProperty<IMDEventWorkspace>(
"OutputWorkspace", "", Direction::Output),
"Name of the output MDWorkspace.");
// Set the box controller properties
this->initBoxControllerProps("2", 500, 16);
}
//----------------------------------------------------------------------------------------------
/** Create the output MDWorkspace from a list of input
*
* @param inputs :: list of names of input MDWorkspaces
*/
void MergeMD::createOutputWorkspace(std::vector<std::string> &inputs) {
std::vector<std::string>::iterator it = inputs.begin();
for (; it != inputs.end(); it++) {
IMDEventWorkspace_sptr ws = boost::dynamic_pointer_cast<IMDEventWorkspace>(
AnalysisDataService::Instance().retrieve(*it));
if (!ws)
throw std::invalid_argument(
"Workspace " + *it +
" is not a MDEventWorkspace. Cannot merge this workspace.");
else
m_workspaces.push_back(ws);
81
82
83
84
85
86
87
88
89
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
if (m_workspaces.empty())
throw std::invalid_argument("No valid m_workspaces specified.");
// Number of dimensions, start with the 0th one. They all have to match # of
// dims anyway
IMDEventWorkspace_const_sptr ws0 = m_workspaces[0];
size_t numDims = ws0->getNumDims();
// Extents to create.
std::vector<coord_t> dimMin(numDims, +1e30f);
std::vector<coord_t> dimMax(numDims, -1e30f);
// Validate each workspace
for (size_t i = 0; i < m_workspaces.size(); i++) {
IMDEventWorkspace_const_sptr ws = m_workspaces[i];
if (ws->getNumDims() != numDims)
throw std::invalid_argument(
"Workspace " + ws->name() +
" does not match the number of dimensions of the others (" +
Strings::toString(ws->getNumDims()) + ", expected " +
Strings::toString(numDims) + ")");
if (ws->getEventTypeName() != ws0->getEventTypeName())
throw std::invalid_argument(
"Workspace " + ws->name() +
" does not match the MDEvent type of the others (" +
ws->getEventTypeName() + ", expected " + ws0->getEventTypeName() +
")");
for (size_t d = 0; d < numDims; d++) {
IMDDimension_const_sptr dim = ws->getDimension(d);
IMDDimension_const_sptr dim0 = ws0->getDimension(d);
if (dim->getName() != dim0->getName())
throw std::invalid_argument(
"Workspace " + ws->name() + " does not have the same dimension " +
Strings::toString(d) + " as the others (" + dim->getName() +
", expected " + dim0->getName() + ")");
// Find the extents
if (dim->getMaximum() > dimMax[d])
dimMax[d] = dim->getMaximum();
if (dim->getMinimum() < dimMin[d])
dimMin[d] = dim->getMinimum();
}
// OK, now create the blank MDWorkspace
// Have the factory create it
out = MDEventFactory::CreateMDWorkspace(numDims, ws0->getEventTypeName());
// Give all the dimensions
for (size_t d = 0; d < numDims; d++) {
IMDDimension_const_sptr dim0 = ws0->getDimension(d);
MDHistoDimension *dim = new MDHistoDimension(
dim0->getName(), dim0->getDimensionId(), dim0->getMDFrame(), dimMin[d],
dimMax[d], dim0->getNBins());
out->addDimension(MDHistoDimension_sptr(dim));
}
// Initialize it using the dimension
out->initialize();
// Set the box controller settings from the properties
this->setBoxController(out->getBoxController());
// Perform the initial box splitting
out->splitBox();
// copy experiment infos
uint16_t nExperiments(0);
if (m_workspaces.size() > std::numeric_limits<uint16_t>::max())
throw std::invalid_argument(
"currently we can not combine more then 65535 experiments");
else
nExperiments = static_cast<uint16_t>(m_workspaces.size());
for (uint16_t i = 0; i < nExperiments; i++) {
uint16_t nWSexperiments = m_workspaces[i]->getNumExperimentInfo();
for (uint16_t j = 0; j < nWSexperiments; j++) {
API::ExperimentInfo_sptr ei = API::ExperimentInfo_sptr(
m_workspaces[i]->getExperimentInfo(j)->cloneExperimentInfo());
out->addExperimentInfo(ei);
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
}
//----------------------------------------------------------------------------------------------
/** Perform the adding.
* Will do out += ws
*
* @param ws :: MDEventWorkspace to clone
*/
template <typename MDE, size_t nd>
void MergeMD::doPlus(typename MDEventWorkspace<MDE, nd>::sptr ws) {
// CPUTimer tim;
typename MDEventWorkspace<MDE, nd>::sptr ws1 =
boost::dynamic_pointer_cast<MDEventWorkspace<MDE, nd>>(out);
typename MDEventWorkspace<MDE, nd>::sptr ws2 = ws;
if (!ws1 || !ws2)
throw std::runtime_error("Incompatible workspace types passed to MergeMD.");
MDBoxBase<MDE, nd> *box1 = ws1->getBox();
MDBoxBase<MDE, nd> *box2 = ws2->getBox();
// How many events you started with
size_t initial_numEvents = ws1->getNPoints();
// Make a leaf-only iterator through all boxes with events in the RHS
// workspace
std::vector<API::IMDNode *> boxes;
box2->getBoxes(boxes, 1000, true);
int numBoxes = int(boxes.size());
bool fileBasedSource(false);
if (ws2->isFileBacked())
fileBasedSource = true;
// Add the boxes in parallel. They should be spread out enough on each
// core to avoid stepping on each other.
// cppcheck-suppress syntaxError
PRAGMA_OMP( parallel for if (!ws2->isFileBacked()) )
for (int i = 0; i < numBoxes; i++) {
PARALLEL_START_INTERUPT_REGION
MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(boxes[i]);
if (box) {
// Copy the events from WS2 and add them into WS1
const std::vector<MDE> &events = box->getConstEvents();
// Add events, with bounds checking
box1->addEvents(events);
box->clear();
else
box->releaseEvents();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
// Progress * prog2 = new Progress(this, 0.4, 0.9, 100);
Progress *prog2 = NULL;
ThreadScheduler *ts = new ThreadSchedulerFIFO();
ThreadPool tp(ts, 0, prog2);
ws1->splitAllIfNeeded(ts);
// prog2->resetNumSteps( ts->size(), 0.4, 0.6);
// Set a marker that the file-back-end needs updating if the # of events
// changed.
if (ws1->getNPoints() != initial_numEvents)
ws1->setFileNeedsUpdating(true);
//
// std::cout << tim << " to add workspace " << ws2->name() << std::endl;
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void MergeMD::exec() {
CPUTimer tim;
// Check that all input workspaces exist and match in certain important ways
const std::vector<std::string> inputs_orig = getProperty("InputWorkspaces");
// This will hold the inputs, with the groups separated off
std::vector<std::string> inputs;
for (size_t i = 0; i < inputs_orig.size(); i++) {
WorkspaceGroup_sptr wsgroup =
AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
inputs_orig[i]);
if (wsgroup) { // Workspace group
std::vector<std::string> group = wsgroup->getNames();
inputs.insert(inputs.end(), group.begin(), group.end());
} else {
// Single workspace
inputs.push_back(inputs_orig[i]);
if (inputs.size() == 1) {
g_log.error("Only one input workspace specified");
throw std::invalid_argument("Only one input workspace specified");
}
// Create a blank output workspace
this->createOutputWorkspace(inputs);
// Run PlusMD on each of the input workspaces, in order.
double progStep = 1.0 / double(m_workspaces.size());
for (size_t i = 0; i < m_workspaces.size(); i++) {
g_log.information() << "Adding workspace " << m_workspaces[i]->name()
<< std::endl;
progress(double(i) * progStep, m_workspaces[i]->name());
CALL_MDEVENT_FUNCTION(doPlus, m_workspaces[i]);
this->progress(0.95, "Refreshing cache");
out->refreshCache();
this->setProperty("OutputWorkspace", out);
g_log.debug() << tim << " to merge all workspaces." << std::endl;
}
} // namespace Mantid
} // namespace MDAlgorithms