Newer
Older
TODO: Enter a full wiki-markup description of your algorithm here. You can then
use the Build/wiki_maker.py script to generate your full wiki page.
#include "MantidDataHandling/LoadILLReflectometry.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/Axis.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/MatrixWorkspace.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidGeometry/Instrument/ComponentHelper.h"
namespace Mantid {
namespace DataHandling {
using namespace Kernel;
using namespace API;
using namespace NeXus;
// Register the algorithm into the AlgorithmFactory
DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadILLReflectometry)
//----------------------------------------------------------------------------------------------
LoadILLReflectometry::LoadILLReflectometry()
: m_numberOfTubes{0}, // number of tubes - X
m_numberOfPixelsPerTube{0}, // number of pixels per tube - Y
m_numberOfChannels{0}, // time channels - Z
m_numberOfHistograms{0}, m_wavelength{0}, m_channelWidth{0},
m_supportedInstruments{"D17"} {}
//----------------------------------------------------------------------------------------------
/// Algorithm's name for identification. @see Algorithm::name
const std::string LoadILLReflectometry::name() const {
return "LoadILLReflectometry";
/// Algorithm's version for identification. @see Algorithm::version
int LoadILLReflectometry::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string LoadILLReflectometry::category() const {
}
/**
* Return the confidence with with this algorithm can load the file
* @param descriptor A descriptor for the file
* @returns An integer specifying the confidence level. 0 indicates it will not
* be used
*/
int LoadILLReflectometry::confidence(
Kernel::NexusDescriptor &descriptor) const {
// fields existent only at the ILL
if (descriptor.pathExists("/entry0/wavelength") // ILL
&& descriptor.pathExists("/entry0/experiment_identifier") // ILL
&& descriptor.pathExists("/entry0/mode") // ILL
&& descriptor.pathExists("/entry0/instrument/Chopper1") // TO BE DONE
&& descriptor.pathExists("/entry0/instrument/Chopper2") // ???
) {
return 80;
} else {
return 0;
}
}
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void LoadILLReflectometry::init() {
declareProperty(
make_unique<FileProperty>("Filename", "", FileProperty::Load, ".nxs"),
"File path of the Data file to load");
declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"The name to use for the output workspace");
//----------------------------------------------------------------------------------------------
/**
* Run the Child Algorithm LoadInstrument.
*/
void LoadILLReflectometry::runLoadInstrument() {
IAlgorithm_sptr loadInst = createChildAlgorithm("LoadInstrument");
// Now execute the Child Algorithm. Catch and log any error, but don't stop.
try {
loadInst->setPropertyValue("InstrumentName", m_instrumentName);
loadInst->setProperty("RewriteSpectraMap",
Mantid::Kernel::OptionalBool(true));
loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", m_localWorkspace);
loadInst->execute();
} catch (std::runtime_error &e) {
g_log.information()
<< "Unable to successfully run LoadInstrument Child Algorithm : "
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
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void LoadILLReflectometry::exec() {
// Retrieve filename
std::string filenameData = getPropertyValue("Filename");
// open the root node
NeXus::NXRoot dataRoot(filenameData);
NXEntry firstEntry = dataRoot.openFirstEntry();
// Load Monitor details: n. monitors x monitor contents
std::vector<std::vector<int>> monitorsData = loadMonitors(firstEntry);
// Load Data details (number of tubes, channels, etc)
loadDataDetails(firstEntry);
std::string instrumentPath = m_loader.findInstrumentNexusPath(firstEntry);
setInstrumentName(firstEntry, instrumentPath);
initWorkSpace(firstEntry, monitorsData);
g_log.debug("Building properties...");
loadNexusEntriesIntoProperties(filenameData);
g_log.debug("Loading data...");
loadDataIntoTheWorkSpace(firstEntry, monitorsData);
// load the instrument from the IDF if it exists
g_log.debug("Loading instrument definition...");
runLoadInstrument();
// 1) Move
// Get distance and tilt angle stored in nexus file
// Mantid way
//// auto angleProp =
/// dynamic_cast<PropertyWithValue<double>*>(m_localWorkspace->run().getProperty("dan.value"));
// Nexus way
double angle =
firstEntry.getFloat("instrument/dan/value"); // detector angle in degrees
double distance = firstEntry.getFloat(
"instrument/det/value"); // detector distance in millimeter
distance /= 1000.0; // convert to meter
g_log.debug() << "Moving detector at angle " << angle << " and distance "
placeDetector(distance, angle);
// Set the channel width property
auto channel_width = dynamic_cast<PropertyWithValue<double> *>(
m_localWorkspace->run().getProperty("monitor1.time_of_flight_0"));
m_localWorkspace->mutableRun().addProperty<double>(
"channel_width", *channel_width, true); // overwrite
// Set the output workspace property
setProperty("OutputWorkspace", m_localWorkspace);
}
/**
* Set member variable with the instrument name
*/
void LoadILLReflectometry::setInstrumentName(
const NeXus::NXEntry &firstEntry, const std::string &instrumentNamePath) {
if (instrumentNamePath == "") {
std::string message("Cannot set the instrument name from the Nexus file!");
g_log.error(message);
throw std::runtime_error(message);
}
m_instrumentName =
m_loader.getStringFromNexusPath(firstEntry, instrumentNamePath + "/name");
boost::to_upper(m_instrumentName); // "D17" in file, keep it upper case.
g_log.debug() << "Instrument name set to: " + m_instrumentName << '\n';
}
/**
* Creates the workspace and initialises member variables with
* the corresponding values
*
* @param entry :: The Nexus entry
* @param monitorsData :: Monitors data already loaded
*
*/
void LoadILLReflectometry::initWorkSpace(
NeXus::NXEntry & /*entry*/, std::vector<std::vector<int>> monitorsData) {
// dim0 * m_numberOfPixelsPerTube is the total number of detectors
m_numberOfHistograms = m_numberOfTubes * m_numberOfPixelsPerTube;
g_log.debug() << "NumberOfTubes: " << m_numberOfTubes << '\n';
g_log.debug() << "NumberOfPixelsPerTube: " << m_numberOfPixelsPerTube << '\n';
g_log.debug() << "NumberOfChannels: " << m_numberOfChannels << '\n';
g_log.debug() << "Monitors: " << monitorsData.size() << '\n';
g_log.debug() << "Monitors[0]: " << monitorsData[0].size() << '\n';
g_log.debug() << "Monitors[1]: " << monitorsData[1].size() << '\n';
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
// Now create the output workspace
m_localWorkspace = WorkspaceFactory::Instance().create(
"Workspace2D", m_numberOfHistograms + monitorsData.size(),
m_numberOfChannels + 1, m_numberOfChannels);
m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
m_localWorkspace->setYUnitLabel("Counts");
}
/**
* Load Data details (number of tubes, channels, etc)
* @param entry First entry of nexus file
*/
void LoadILLReflectometry::loadDataDetails(NeXus::NXEntry &entry) {
// read in the data
NXData dataGroup = entry.openNXData("data");
NXInt data = dataGroup.openIntData();
m_numberOfTubes = static_cast<size_t>(data.dim0());
m_numberOfPixelsPerTube = static_cast<size_t>(data.dim1());
m_numberOfChannels = static_cast<size_t>(data.dim2());
}
/**
* Load monitors data found in nexus file
*
* @param entry :: The Nexus entry
*
*/
std::vector<std::vector<int>>
LoadILLReflectometry::loadMonitors(NeXus::NXEntry &entry) {
// read in the data
g_log.debug("Fetching monitor data...");
NXData dataGroup = entry.openNXData("monitor1/data");
NXInt data = dataGroup.openIntData();
// load the counts from the file into memory
data.load();
std::vector<std::vector<int>> monitors(
1); // vector of monitors with one entry
std::vector<int> monitor1(data(), data() + data.size());
monitors[0].swap(monitor1);
// There is two monitors in data file, but the second one seems to be always 0
dataGroup = entry.openNXData("monitor2/data");
data = dataGroup.openIntData();
data.load();
std::vector<int> monitor2(data(), data() + data.size());
monitors.push_back(monitor2);
return monitors;
}
/**
* Load data found in nexus file
*
* @param entry :: The Nexus entry
* @param monitorsData :: Monitors data already loaded
*
*/
void LoadILLReflectometry::loadDataIntoTheWorkSpace(
NeXus::NXEntry &entry, std::vector<std::vector<int>> monitorsData) {
m_wavelength = entry.getFloat("wavelength");
double ei = m_loader.calculateEnergy(m_wavelength);
m_localWorkspace->mutableRun().addProperty<double>("Ei", ei,
true); // overwrite
// read in the data
NXData dataGroup = entry.openNXData("data");
NXInt data = dataGroup.openIntData();
// load the counts from the file into memory
data.load();
// Assign calculated bins to first X axis
//// m_localWorkspace->dataX(0).assign(detectorTofBins.begin(),
/// detectorTofBins.end());
size_t spec = 0;
size_t nb_monitors = monitorsData.size();
Progress progress(this, 0, 1,
m_numberOfTubes * m_numberOfPixelsPerTube + nb_monitors);
// Assign tof values to first X axis
// 1) Get some parameters from nexus file and properties
// Note : This should be changed following future D17/ILL nexus file
// improvement.
const std::string propTOF0 = "monitor1.time_of_flight_0";
auto tof_channel_width_prop = dynamic_cast<PropertyWithValue<double> *>(
m_localWorkspace->run().getProperty(propTOF0));
if (!tof_channel_width_prop)
throw std::runtime_error("Could not cast (interpret) the property " +
propTOF0 + " (channel width) as a floating point "
m_channelWidth = *tof_channel_width_prop; /* PAR1[95] */
const std::string propTOF2 = "monitor1.time_of_flight_2";
auto tof_delay_prop = dynamic_cast<PropertyWithValue<double> *>(
m_localWorkspace->run().getProperty(propTOF2));
if (!tof_delay_prop)
throw std::runtime_error("Could not cast (interpret) the property " +
propTOF2 +
" (ToF delay) as a floating point value.");
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
double tof_delay = *tof_delay_prop; /* PAR1[96] */
double POFF = entry.getFloat("instrument/VirtualChopper/poff"); /* par1[54] */
double open_offset =
entry.getFloat("instrument/VirtualChopper/open_offset"); /* par1[56] */
double mean_chop_1_phase = 0.0;
double mean_chop_2_phase = 0.0;
// [30/09/14] Test on availability of VirtualChopper data
double chop1_speed = entry.getFloat(
"instrument/VirtualChopper/chopper1_speed_average"); /* PAR2[109] */
if (chop1_speed != 0.0) {
// Virtual Chopper entries are valid
// double mean_chop_1_phase =
// entry.getFloat("instrument/VirtualChopper/chopper1_phase_average"); /*
// PAR2[110] */
// this entry seems to be wrong for now, we use the old one instead [YR
// 5/06/2014]
mean_chop_1_phase = entry.getFloat("instrument/Chopper1/phase");
mean_chop_2_phase = entry.getFloat(
"instrument/VirtualChopper/chopper2_phase_average"); /* PAR2[114] */
} else {
// Use Chopper values instead
chop1_speed =
entry.getFloat("instrument/Chopper1/rotation_speed"); /* PAR2[109] */
mean_chop_1_phase = entry.getFloat("instrument/Chopper1/phase");
mean_chop_2_phase = entry.getFloat("instrument/Chopper2/phase");
}
g_log.debug() << "m_numberOfChannels: " << m_numberOfChannels << '\n';
g_log.debug() << "m_channelWidth: " << m_channelWidth << '\n';
g_log.debug() << "tof_delay: " << tof_delay << '\n';
g_log.debug() << "POFF: " << POFF << '\n';
g_log.debug() << "open_offset: " << open_offset << '\n';
g_log.debug() << "mean_chop_2_phase: " << mean_chop_2_phase << '\n';
g_log.debug() << "mean_chop_1_phase: " << mean_chop_1_phase << '\n';
g_log.debug() << "chop1_speed: " << chop1_speed << '\n';
double t_TOF2 = 0.0;
if (chop1_speed == 0.0) {
g_log.debug() << "Warning: chop1_speed is null.\n";
// stay with t_TOF2 to O.0
} else {
// Thanks to Miguel Gonzales/ILL for this TOF formula
t_TOF2 = -1.e6 * 60.0 * (POFF - 45.0 + mean_chop_2_phase -
mean_chop_1_phase + open_offset) /
(2.0 * 360 * chop1_speed);
}
g_log.debug() << "t_TOF2: " << t_TOF2 << '\n';
// 2) Compute tof values
for (size_t timechannelnumber = 0; timechannelnumber <= m_numberOfChannels;
++timechannelnumber) {
double t_TOF1 =
(static_cast<int>(timechannelnumber) + 0.5) * m_channelWidth +
tof_delay;
m_localWorkspace->dataX(0)[timechannelnumber] = t_TOF1 + t_TOF2;
}
// Load monitors
for (size_t im = 0; im < nb_monitors; im++) {
if (im > 0) {
m_localWorkspace->dataX(im) = m_localWorkspace->readX(0);
// Assign Y
int *monitor_p = monitorsData[im].data();
m_localWorkspace->dataY(im)
.assign(monitor_p, monitor_p + m_numberOfChannels);
// Then Tubes
for (size_t i = 0; i < m_numberOfTubes; ++i) {
for (size_t j = 0; j < m_numberOfPixelsPerTube; ++j) {
// just copy the time binning axis to every spectra
m_localWorkspace->dataX(spec + nb_monitors) = m_localWorkspace->readX(0);
//// Assign Y
int *data_p = &data(static_cast<int>(i), static_cast<int>(j), 0);
m_localWorkspace->dataY(spec + nb_monitors)
.assign(data_p, data_p + m_numberOfChannels);
// Assign Error
MantidVec &E = m_localWorkspace->dataE(spec + nb_monitors);
std::transform(data_p, data_p + m_numberOfChannels, E.begin(),
LoadHelper::calculateStandardError);
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
} // for m_numberOfTubes
} // LoadILLIndirect::loadDataIntoTheWorkSpace
/**
* Use the LoadHelper utility to load most of the nexus entries into workspace
* log properties
*/
void LoadILLReflectometry::loadNexusEntriesIntoProperties(
std::string nexusfilename) {
API::Run &runDetails = m_localWorkspace->mutableRun();
// Open NeXus file
NXhandle nxfileID;
NXstatus stat = NXopen(nexusfilename.c_str(), NXACC_READ, &nxfileID);
if (stat == NX_ERROR) {
g_log.debug() << "convertNexusToProperties: Error loading "
<< nexusfilename;
throw Kernel::Exception::FileError("Unable to open File:", nexusfilename);
}
m_loader.addNexusFieldsToWsRun(nxfileID, runDetails);
// Add also "Facility", as asked
runDetails.addProperty("Facility", std::string("ILL"));
stat = NXclose(&nxfileID);
}
/**
* Utility to center detector.
*/
void LoadILLReflectometry::centerDetector(double xCenter) {
std::string componentName("uniq_detector");
V3D pos = m_loader.getComponentPosition(m_localWorkspace, componentName);
// TODO confirm!
pos.setX(pos.X() - xCenter);
m_loader.moveComponent(m_localWorkspace, componentName, pos);
}
/**
* Utility to place detector in space, according to data file
*/
void LoadILLReflectometry::placeDetector(double distance /* meter */,
double angle /* degree */) {
const double deg2rad = M_PI / 180.0;
std::string componentName("uniq_detector");
V3D pos = m_loader.getComponentPosition(m_localWorkspace, componentName);
// double r, theta, phi;
// pos.getSpherical(r, theta, phi);
double angle_rad = angle * deg2rad;
V3D newpos(distance * sin(angle_rad), pos.Y(), distance * cos(angle_rad));
m_loader.moveComponent(m_localWorkspace, componentName, newpos);
// Apply a local rotation to stay perpendicular to the beam
const V3D axis(0.0, 1.0, 0.0);
Quat rotation(angle, axis);
m_loader.rotateComponent(m_localWorkspace, componentName, rotation);
}
} // namespace DataHandling