-
Notifications
You must be signed in to change notification settings - Fork 123
/
ConvertToDiffractionMDWorkspace.cpp
645 lines (560 loc) · 25.2 KB
/
ConvertToDiffractionMDWorkspace.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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
202
203
204
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
240
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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
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
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
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
572
573
574
575
576
577
578
579
580
581
582
583
584
585
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
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
#include "MantidMDAlgorithms/ConvertToDiffractionMDWorkspace.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/IMDEventWorkspace.h"
#include "MantidAPI/MemoryManager.h"
#include "MantidAPI/Progress.h"
#include "MantidAPI/WorkspaceUnitValidator.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/MDEventFactory.h"
#include "MantidDataObjects/MDEventWorkspace.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 "MantidKernel/ListValidator.h"
#include "MantidKernel/ConfigService.h"
using namespace Mantid;
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::DataObjects;
using namespace Mantid::Geometry;
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(nullptr),
m_extentsMax(nullptr) // 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(make_unique<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(make_unique<WorkspaceProperty<IMDEventWorkspace>>(
"OutputWorkspace", "", Direction::Output),
"Name of the output MDEventWorkspace. If the workspace "
"already exists, then the events will be added to it.");
declareProperty(
make_unique<PropertyWithValue<bool>>("Append", false, Direction::Input),
"Append events to the output workspace. The workspace is replaced if "
"unchecked.");
declareProperty(make_unique<PropertyWithValue<bool>>("ClearInputWorkspace",
false, Direction::Input),
"Clear the events from the input workspace during "
"conversion, to save memory.");
declareProperty(
make_unique<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 significantly more memory!");
std::vector<std::string> propOptions{"Q (lab frame)", "Q (sample frame)",
"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(make_unique<PropertyWithValue<bool>>("LorentzCorrection",
false, Direction::Input),
"Correct the weights of events 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*/);
declareProperty(
make_unique<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{-50, +50};
declareProperty(
Kernel::make_unique<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 auto &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;
}
// 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;
double qSign = -1.0;
std::string convention =
ConfigService::Instance().getString("Q.convention");
if (convention == "Crystallography")
qSign = 1.0;
Q_dir_lab_frame *= qSign;
// 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
}
/** 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
auto it = events.begin();
auto 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, static_cast<size_t>(2e8));
}
}
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,
static_cast<coord_t>(extents[d * 2]),
static_cast<coord_t>(extents[d * 2 + 1]), 10);
ws->addDimension(MDHistoDimension_sptr(dim));
}
ws->initialize();
// 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)
ws->splitBox();
// 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));
}
ws->splitBox();
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();
}
// 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));
}
// 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;
}
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 (auto &stat : stats)
g_log.information() << stat << "\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