Newer
Older
#include "MantidAPI/IMDEventWorkspace.h"
#include "MantidAPI/Progress.h"
#include "MantidAPI/WorkspaceValidators.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidGeometry/Crystal/OrientedLattice.h"
#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
#include "MantidGeometry/MDGeometry/MDFrameFactory.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/CPUTimer.h"
#include "MantidKernel/FunctionTask.h"
#include "MantidKernel/PhysicalConstants.h"
#include "MantidKernel/ProgressText.h"
#include "MantidKernel/System.h"
#include "MantidKernel/Timer.h"
#include "MantidKernel/UnitLabelTypes.h"
#include "MantidMDAlgorithms/ConvertToDiffractionMDWorkspace.h"
#include "MantidDataObjects/MDEventFactory.h"
#include "MantidDataObjects/MDEventWorkspace.h"
#include "MantidAPI/MemoryManager.h"
#include "MantidKernel/ListValidator.h"
using namespace Mantid;
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::DataObjects;
using namespace Mantid::Geometry;
29
30
31
32
33
34
35
36
37
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
80
81
82
83
84
85
86
87
88
89
namespace Mantid {
namespace MDAlgorithms {
bool DODEBUG = true;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(ConvertToDiffractionMDWorkspace)
//----------------------------------------------------------------------------------------------
/** Constructor
*/
ConvertToDiffractionMDWorkspace::ConvertToDiffractionMDWorkspace()
: ClearInputWorkspace(false), // imput workspace should be left untouched
OneEventPerBin(false), // it is very expensive otherwise
Append(true), // append data to existing target MD workspace if one exist
LorentzCorrection(false), // not doing Lorents
l1(1.), beamline_norm(1.), failedDetectorLookupCount(0),
m_extentsMin(NULL),
m_extentsMax(NULL) // will be allocated in exec using nDims
{}
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void ConvertToDiffractionMDWorkspace::init() {
// Input units must be TOF
auto validator = boost::make_shared<API::WorkspaceUnitValidator>("TOF");
declareProperty(new WorkspaceProperty<MatrixWorkspace>(
"InputWorkspace", "", Direction::Input, validator),
"An input workspace in time-of-flight. If you specify a "
"Workspace2D, it gets converted to "
"an EventWorkspace using ConvertToEventWorkspace.");
declareProperty(new WorkspaceProperty<IMDEventWorkspace>(
"OutputWorkspace", "", Direction::Output),
"Name of the output MDEventWorkspace. If the workspace "
"already exists, then the events will be added to it.");
declareProperty(
new PropertyWithValue<bool>("Append", false, Direction::Input),
"Append events to the output workspace. The workspace is replaced if "
"unchecked.");
declareProperty(new PropertyWithValue<bool>("ClearInputWorkspace", false,
Direction::Input),
"Clear the events from the input workspace during "
"conversion, to save memory.");
declareProperty(
new PropertyWithValue<bool>("OneEventPerBin", false, Direction::Input),
"Use the histogram representation (event for event workspaces).\n"
"One MDEvent will be created for each histogram bin (even empty ones).\n"
"Warning! This can use signficantly more memory!");
std::vector<std::string> propOptions;
propOptions.push_back("Q (lab frame)");
propOptions.push_back("Q (sample frame)");
propOptions.push_back("HKL");
declareProperty(
"OutputDimensions", "Q (lab frame)",
boost::make_shared<StringListValidator>(propOptions),
"What will be the dimensions of the output workspace?\n"
" Q (lab frame): Wave-vector change of the lattice in the lab frame.\n"
" Q (sample frame): Wave-vector change of the lattice in the frame of "
"the sample (taking out goniometer rotation).\n"
" HKL: Use the sample's UB matrix to convert to crystal's HKL indices.");
declareProperty(
new PropertyWithValue<bool>("LorentzCorrection", false, Direction::Input),
"Correct the weights of events with by multiplying by the Lorentz "
"formula: sin(theta)^2 / lambda^4");
// Box controller properties. These are the defaults
this->initBoxControllerProps("2" /*SplitInto*/, 1500 /*SplitThreshold*/,
20 /*MaxRecursionDepth*/);
new PropertyWithValue<int>("MinRecursionDepth", 0),
"Optional. If specified, then all the boxes will be split to this "
"minimum recursion depth. 1 = one level of splitting, etc.\n"
"Be careful using this since it can quickly create a huge number of "
"boxes = (SplitInto ^ (MinRercursionDepth x NumDimensions)).\n"
"But setting this property equal to MaxRecursionDepth property is "
"necessary if one wants to generate multiple file based workspaces in "
"order to merge them later\n");
setPropertyGroup("MinRecursionDepth", getBoxSettingsGroupName());
std::vector<double> extents(2, 0);
extents[0] = -50;
extents[1] = +50;
declareProperty(new ArrayProperty<double>("Extents", extents),
"A comma separated list of min, max for each dimension,\n"
"specifying the extents of each dimension. Optional, default "
"+-50 in each dimension.");
setPropertyGroup("Extents", getBoxSettingsGroupName());
}
/// Our MDLeanEvent dimension
typedef DataObjects::MDLeanEvent<3> MDE;
//----------------------------------------------------------------------------------------------
/** Convert one spectrum to DataObjects.
* Depending on options, it uses the histogram view or the
* pure event view.
* Then another method converts to 3D q-space and adds it to the
*MDEventWorkspace
*
* @param workspaceIndex :: index into the workspace
*/
void ConvertToDiffractionMDWorkspace::convertSpectrum(int workspaceIndex) {
if (m_inEventWS && !OneEventPerBin) {
// ---------- Convert events directly -------------------------
EventList &el = m_inEventWS->getEventList(workspaceIndex);
// Call the right templated function
switch (el.getEventType()) {
case TOF:
this->convertEventList<TofEvent>(workspaceIndex, el);
break;
case WEIGHTED:
this->convertEventList<WeightedEvent>(workspaceIndex, el);
break;
case WEIGHTED_NOTIME:
this->convertEventList<WeightedEventNoTime>(workspaceIndex, el);
break;
default:
throw std::runtime_error("EventList had an unexpected data type!");
} else {
// ----- Workspace2D, or use the Histogram representation of EventWorkspace
// ------------
// Construct a new event list
EventList el;
// Create the events using the bins
const ISpectrum *inSpec = m_inWS->getSpectrum(workspaceIndex);
// If OneEventPerBin, generate exactly 1 event per bin, including zeros.
// If !OneEventPerBin, generate up to 10 events per bin, excluding zeros
el.createFromHistogram(
inSpec, OneEventPerBin /* Generate zeros */,
!OneEventPerBin /* Multiple events */,
(OneEventPerBin ? 1 : 10) /* Max of this many events per bin */);
// Perform the conversion on this temporary event list
this->convertEventList<WeightedEventNoTime>(workspaceIndex, el);
}
//----------------------------------------------------------------------------------------------
/** Convert an event list to 3D q-space and add it to the MDEventWorkspace
*
* @tparam T :: the type of event in the input EventList (TofEvent,
*WeightedEvent, etc.)
* @param workspaceIndex :: the workspace index
* @param el :: reference to the event list
*/
template <class T>
void ConvertToDiffractionMDWorkspace::convertEventList(int workspaceIndex,
EventList &el) {
size_t numEvents = el.getNumberEvents();
DataObjects::MDBoxBase<DataObjects::MDLeanEvent<3>, 3> *box = ws->getBox();
// Get the position of the detector there.
const std::set<detid_t> &detectors = el.getDetectorIDs();
if (!detectors.empty()) {
// Get the detector (might be a detectorGroup for multiple detectors)
// or might return an exception if the detector is not in the instrument
// definition
IDetector_const_sptr det;
try {
det = m_inWS->getDetector(workspaceIndex);
} catch (Exception::NotFoundError &) {
this->failedDetectorLookupCount++;
return;
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Vector between the sample and the detector
V3D detPos = det->getPos() - samplePos;
// Neutron's total travelled distance
double distance = detPos.norm() + l1;
// Detector direction normalized to 1
V3D detDir = detPos / detPos.norm();
// The direction of momentum transfer in the inelastic convention ki-kf
// = input beam direction (normalized to 1) - output beam direction
// (normalized to 1)
V3D Q_dir_lab_frame = beamDir - detDir;
// Multiply by the rotation matrix to convert to Q in the sample frame (take
// out goniometer rotation)
// (or to HKL, if that's what the matrix is)
V3D Q_dir = mat * Q_dir_lab_frame;
// For speed we extract the components.
coord_t Q_dir_x = coord_t(Q_dir.X());
coord_t Q_dir_y = coord_t(Q_dir.Y());
coord_t Q_dir_z = coord_t(Q_dir.Z());
// For lorentz correction, calculate sin(theta))^2
double sin_theta_squared = 0;
if (LorentzCorrection) {
// Scattering angle = 2 theta = angle between neutron beam direction and
// the detector (scattering) direction
// The formula for Lorentz Correction is sin(theta), i.e. sin(half the
// scattering angle)
double theta = detDir.angle(beamDir) / 2.0;
sin_theta_squared = sin(theta);
sin_theta_squared = sin_theta_squared * sin_theta_squared; // square it
}
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/** Constant that you divide by tof (in usec) to get wavenumber in ang^-1 :
* Wavenumber (in ang^-1) = (PhysicalConstants::NeutronMass * distance) /
* ((tof (in usec) * 1e-6) * PhysicalConstants::h_bar) * 1e-10; */
const double wavenumber_in_angstrom_times_tof_in_microsec =
(PhysicalConstants::NeutronMass * distance * 1e-10) /
(1e-6 * PhysicalConstants::h_bar);
// PARALLEL_CRITICAL( convert_tester_output ) { std::cout << "Spectrum " <<
// el.getSpectrumNo() << " beamDir = " << beamDir << " detDir = " << detDir
// << " Q_dir = " << Q_dir << " conversion factor " <<
// wavenumber_in_angstrom_times_tof_in_microsec << std::endl; }
// g_log.information() << wi << " : " << el.getNumberEvents() << " events.
// Pos is " << detPos << std::endl;
// g_log.information() << Q_dir.norm() << " Qdir norm" << std::endl;
// This little dance makes the getting vector of events more general (since
// you can't overload by return type).
typename std::vector<T> *events_ptr;
getEventsFrom(el, events_ptr);
typename std::vector<T> &events = *events_ptr;
// Iterators to start/end
typename std::vector<T>::iterator it = events.begin();
typename std::vector<T>::iterator it_end = events.end();
for (; it != it_end; it++) {
// Get the wavenumber in ang^-1 using the previously calculated constant.
coord_t wavenumber =
coord_t(wavenumber_in_angstrom_times_tof_in_microsec / it->tof());
// Q vector = K_final - K_initial = wavenumber * (output_direction -
// input_direction)
coord_t center[3] = {Q_dir_x * wavenumber, Q_dir_y * wavenumber,
Q_dir_z * wavenumber};
// Check that the event is within bounds
if (center[0] < m_extentsMin[0] || center[0] >= m_extentsMax[0])
continue;
if (center[1] < m_extentsMin[1] || center[1] >= m_extentsMax[1])
continue;
if (center[2] < m_extentsMin[2] || center[2] >= m_extentsMax[2])
continue;
if (LorentzCorrection) {
// double lambda = 1.0/wavenumber;
// (sin(theta))^2 / wavelength^4
float correct = float(sin_theta_squared * wavenumber * wavenumber *
wavenumber * wavenumber);
// Push the MDLeanEvent but correct the weight.
box->addEvent(MDE(float(it->weight() * correct),
float(it->errorSquared() * correct * correct),
center));
} else {
// Push the MDLeanEvent with the same weight
box->addEvent(
MDE(float(it->weight()), float(it->errorSquared()), center));
// Clear out the EventList to save memory
if (ClearInputWorkspace) {
// Track how much memory you cleared
size_t memoryCleared = el.getMemorySize();
// Clear it now
el.clear();
// For Linux with tcmalloc, make sure memory goes back, if you've cleared
// 200 Megs
MemoryManager::Instance().releaseFreeMemoryIfAccumulated(memoryCleared,
(size_t)2e8);
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
}
prog->reportIncrement(numEvents, "Adding Events");
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void ConvertToDiffractionMDWorkspace::exec() {
Timer tim, timtotal;
CPUTimer cputim, cputimtotal;
// ---------------------- Extract properties
// --------------------------------------
ClearInputWorkspace = getProperty("ClearInputWorkspace");
Append = getProperty("Append");
std::string OutputDimensions = getPropertyValue("OutputDimensions");
LorentzCorrection = getProperty("LorentzCorrection");
OneEventPerBin = getProperty("OneEventPerBin");
// -------- Input workspace -> convert to Event
// ------------------------------------
m_inWS = getProperty("InputWorkspace");
Workspace2D_sptr m_InWS2D = boost::dynamic_pointer_cast<Workspace2D>(m_inWS);
if (LorentzCorrection) {
API::Run &run = m_inWS->mutableRun();
if (run.hasProperty("LorentzCorrection")) {
Kernel::Property *prop = run.getProperty("LorentzCorrection");
bool lorentzDone = boost::lexical_cast<bool, std::string>(prop->value());
if (lorentzDone) {
LorentzCorrection = false;
g_log.warning() << "Lorentz Correction was already done for this "
"workspace. LorentzCorrection was changed to false."
<< std::endl;
}
m_inEventWS = boost::dynamic_pointer_cast<EventWorkspace>(m_inWS);
// check the input units
if (m_inWS->getAxis(0)->unit()->unitID() != "TOF")
throw std::invalid_argument(
"Input event workspace's X axis must be in TOF units.");
// Try to get the output workspace
IMDEventWorkspace_sptr i_out = getProperty("OutputWorkspace");
ws = boost::dynamic_pointer_cast<
DataObjects::MDEventWorkspace<DataObjects::MDLeanEvent<3>, 3>>(i_out);
// Initalize the matrix to 3x3 identity
mat = Kernel::Matrix<double>(3, 3, true);
// ----------------- Handle the type of output
// -------------------------------------
std::string dimensionNames[3] = {"Q_lab_x", "Q_lab_y", "Q_lab_z"};
Mantid::Kernel::SpecialCoordinateSystem coordinateSystem =
Mantid::Kernel::QLab;
// Setup the MDFrame
auto frameFactory = makeMDFrameFactoryChain();
Mantid::Geometry::MDFrame_uptr frame;
if (OutputDimensions == "Q (sample frame)") {
// Set the matrix based on goniometer angles
mat = m_inWS->mutableRun().getGoniometerMatrix();
// But we need to invert it, since we want to get the Q in the sample frame.
mat.Invert();
// Names
dimensionNames[0] = "Q_sample_x";
dimensionNames[1] = "Q_sample_y";
dimensionNames[2] = "Q_sample_z";
coordinateSystem = Mantid::Kernel::QSample;
// Frame
MDFrameArgument frameArgQSample(QSample::QSampleName, "");
frame = frameFactory->create(frameArgQSample);
} else if (OutputDimensions == "HKL") {
// Set the matrix based on UB etc.
Kernel::Matrix<double> ub =
m_inWS->mutableSample().getOrientedLattice().getUB();
Kernel::Matrix<double> gon = m_inWS->mutableRun().getGoniometerMatrix();
// As per Busing and Levy 1967, q_lab_frame = 2pi * Goniometer * UB * HKL
// Therefore, HKL = (2*pi * Goniometer * UB)^-1 * q_lab_frame
mat = gon * ub;
mat.Invert();
// Divide by 2 PI to account for our new convention, |Q| = 2pi / wl
// (December 2011, JZ)
mat /= (2 * M_PI);
dimensionNames[0] = "H";
dimensionNames[1] = "K";
dimensionNames[2] = "L";
coordinateSystem = Mantid::Kernel::HKL;
MDFrameArgument frameArgQLab(HKL::HKLName, Units::Symbol::RLU.ascii());
frame = frameFactory->create(frameArgQLab);
} else {
MDFrameArgument frameArgQLab(QLab::QLabName, "");
frame = frameFactory->create(frameArgQLab);
}
// Q in the lab frame is the default, so nothing special to do.
if (ws && Append) {
// Check that existing workspace dimensions make sense with the desired one
// (using the name)
if (ws->getDimension(0)->getName() != dimensionNames[0])
throw std::runtime_error("The existing MDEventWorkspace " +
ws->getName() +
" has different dimensions than were requested! "
"Either give a different name for the output, "
"or change the OutputDimensions parameter.");
}
// ------------------- Create the output workspace if needed
// ------------------------
if (!ws || !Append) {
// Create an output workspace with 3 dimensions.
size_t nd = 3;
i_out = DataObjects::MDEventFactory::CreateMDWorkspace(nd, "MDLeanEvent");
ws = boost::dynamic_pointer_cast<DataObjects::MDEventWorkspace3Lean>(i_out);
// ---------------- Get the extents -------------
std::vector<double> extents = getProperty("Extents");
// Replicate a single min,max into several
if (extents.size() == 2)
for (size_t d = 1; d < nd; d++) {
extents.push_back(extents[0]);
extents.push_back(extents[1]);
if (extents.size() != nd * 2)
throw std::invalid_argument(
"You must specify either 2 or 6 extents (min,max).");
// Give all the dimensions
for (size_t d = 0; d < nd; d++) {
MDHistoDimension *dim = new MDHistoDimension(
dimensionNames[d], dimensionNames[d], *frame.get(),
static_cast<coord_t>(extents[d * 2]),
static_cast<coord_t>(extents[d * 2 + 1]), 10);
ws->addDimension(MDHistoDimension_sptr(dim));
// Build up the box controller, using the properties in
// BoxControllerSettingsAlgorithm
BoxController_sptr bc = ws->getBoxController();
this->setBoxController(bc, m_inWS->getInstrument());
// We always want the box to be split (it will reject bad ones)
// Perform minimum recursion depth splitting
int minDepth = this->getProperty("MinRecursionDepth");
int maxDepth = this->getProperty("MaxRecursionDepth");
if (minDepth > maxDepth)
throw std::invalid_argument(
"MinRecursionDepth must be <= MaxRecursionDepth ");
ws->setMinRecursionDepth(size_t(minDepth));
}
if (!ws)
throw std::runtime_error("Error creating a 3D MDEventWorkspace!");
BoxController_sptr bc = ws->getBoxController();
if (!bc)
throw std::runtime_error(
"Output MDEventWorkspace does not have a BoxController!");
// Cache the extents for speed.
m_extentsMin = new coord_t[3];
m_extentsMax = new coord_t[3];
for (size_t d = 0; d < 3; d++) {
m_extentsMin[d] = ws->getDimension(d)->getMinimum();
m_extentsMax[d] = ws->getDimension(d)->getMaximum();
}
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
527
528
// Copy ExperimentInfo (instrument, run, sample) to the output WS
ExperimentInfo_sptr ei(m_inWS->cloneExperimentInfo());
uint16_t runIndex = ws->addExperimentInfo(ei);
UNUSED_ARG(runIndex);
// ------------------- Cache values that are common for all
// ---------------------------
// Extract some parameters global to the instrument
m_inWS->getInstrument()->getInstrumentParameters(l1, beamline, beamline_norm,
samplePos);
beamline_norm = beamline.norm();
beamDir = beamline / beamline.norm();
// To get all the detector ID's
m_inWS->getInstrument()->getDetectors(allDetectors);
// Estimate the number of events in the final workspace
size_t totalEvents = m_inWS->size();
if (m_inEventWS && !OneEventPerBin)
totalEvents = m_inEventWS->getNumberEvents();
prog = boost::make_shared<Progress>(this, 0, 1.0, totalEvents);
// Is the addition of events thread-safe?
bool MultiThreadedAdding = m_inWS->threadSafe();
// Create the thread pool that will run all of these.
ThreadScheduler *ts = new ThreadSchedulerFIFO();
ThreadPool tp(ts, 0);
// To track when to split up boxes
this->failedDetectorLookupCount = 0;
size_t eventsAdded = 0;
size_t approxEventsInOutput = 0;
size_t lastNumBoxes = ws->getBoxController()->getTotalNumMDBoxes();
if (DODEBUG)
g_log.information() << cputim << ": initial setup. There are "
<< lastNumBoxes << " MDBoxes.\n";
for (size_t wi = 0; wi < m_inWS->getNumberHistograms(); wi++) {
// Get an idea of how many events we'll be adding
size_t eventsAdding = m_inWS->blocksize();
if (m_inEventWS && !OneEventPerBin)
eventsAdding = m_inEventWS->getEventList(wi).getNumberEvents();
if (MultiThreadedAdding) {
// Equivalent to calling "this->convertSpectrum(wi)"
boost::function<void()> func =
boost::bind(&ConvertToDiffractionMDWorkspace::convertSpectrum, &*this,
static_cast<int>(wi));
// Give this task to the scheduler
double cost = static_cast<double>(eventsAdding);
ts->push(new FunctionTask(func, cost));
} else {
// Not thread-safe. Just add right now
this->convertSpectrum(static_cast<int>(wi));
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
// Keep a running total of how many events we've added
eventsAdded += eventsAdding;
approxEventsInOutput += eventsAdding;
if (bc->shouldSplitBoxes(approxEventsInOutput, eventsAdded, lastNumBoxes)) {
if (DODEBUG)
g_log.information() << cputim << ": Added tasks worth " << eventsAdded
<< " events. WorkspaceIndex " << wi << std::endl;
// Do all the adding tasks
tp.joinAll();
if (DODEBUG)
g_log.information() << cputim
<< ": Performing the addition of these events.\n";
// Now do all the splitting tasks
ws->splitAllIfNeeded(ts);
if (ts->size() > 0)
prog->doReport("Splitting Boxes");
tp.joinAll();
// Count the new # of boxes.
lastNumBoxes = ws->getBoxController()->getTotalNumMDBoxes();
if (DODEBUG)
g_log.information() << cputim
<< ": Performing the splitting. There are now "
<< lastNumBoxes << " boxes.\n";
eventsAdded = 0;
if (this->failedDetectorLookupCount > 0) {
if (this->failedDetectorLookupCount == 1)
g_log.warning() << "Unable to find a detector for "
<< this->failedDetectorLookupCount
<< " spectrum. It has been skipped." << std::endl;
else
g_log.warning() << "Unable to find detectors for "
<< this->failedDetectorLookupCount
<< " spectra. They have been skipped." << std::endl;
}
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
if (DODEBUG)
g_log.information() << cputim << ": We've added tasks worth " << eventsAdded
<< " events.\n";
tp.joinAll();
if (DODEBUG)
g_log.information() << cputim
<< ": Performing the FINAL addition of these events.\n";
// Do a final splitting of everything
ws->splitAllIfNeeded(ts);
tp.joinAll();
if (DODEBUG)
g_log.information()
<< cputim << ": Performing the FINAL splitting of boxes. There are now "
<< ws->getBoxController()->getTotalNumMDBoxes() << " boxes\n";
// Recount totals at the end.
cputim.reset();
ws->refreshCache();
if (DODEBUG)
g_log.information() << cputim << ": Performing the refreshCache().\n";
// TODO: Centroid in parallel, maybe?
// ws->getBox()->refreshCentroid(NULL);
// if (DODEBUG) g_log.information() << cputim << ": Performing the
// refreshCentroid().\n";
if (DODEBUG) {
g_log.information() << "Workspace has " << ws->getNPoints()
<< " events. This took " << cputimtotal
<< " in total.\n";
std::vector<std::string> stats = ws->getBoxControllerStats();
for (size_t i = 0; i < stats.size(); ++i)
g_log.information() << stats[i] << "\n";
g_log.information() << std::endl;
// Set the special coordinate system.
ws->setCoordinateSystem(coordinateSystem);
// Save the output
setProperty("OutputWorkspace",
boost::dynamic_pointer_cast<IMDEventWorkspace>(ws));
// Clean up
delete[] m_extentsMin;
delete[] m_extentsMax;
}
} // namespace Mantid
} // namespace DataObjects