Mantid
Loading...
Searching...
No Matches
LoadILLIndirect2.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
9#include "MantidAPI/Axis.h"
13#include "MantidAPI/Run.h"
18#include "MantidHistogramData/LinearGenerator.h"
23
24#include <Poco/Path.h>
25#include <boost/algorithm/string.hpp>
26#include <boost/format.hpp>
27#include <cmath>
28#include <nexus/napi.h>
29
30namespace Mantid::DataHandling {
31
32using namespace Kernel;
33using namespace API;
34using namespace NeXus;
35
36// Register the algorithm into the AlgorithmFactory
38
39//----------------------------------------------------------------------------------------------
43 : m_numberOfTubes{16}, m_numberOfChannels{1024}, m_numberOfSimpleDetectors{8}, m_numberOfMonitors{1}, m_bats{false},
44 m_firstTubeAngleRounded{251}, m_supportedInstruments{"IN16B"} {}
45
46//----------------------------------------------------------------------------------------------
48const std::string LoadILLIndirect2::name() const { return "LoadILLIndirect"; }
49
51const std::string LoadILLIndirect2::category() const { return "DataHandling\\Nexus;ILL\\Indirect"; }
52
53//----------------------------------------------------------------------------------------------
54
62
63 // fields existent only at the ILL
64 if (descriptor.pathExists("/entry0/wavelength") // ILL
65 && descriptor.pathExists("/entry0/experiment_identifier") // ILL
66 && descriptor.pathExists("/entry0/mode") // ILL
67 && ((descriptor.pathExists("/entry0/instrument/Doppler/mirror_sense") &&
68 descriptor.pathExists("/entry0/dataSD/SingleD_data")) // IN16B new
69 || (descriptor.pathExists("/entry0/instrument/Doppler/doppler_frequency") &&
70 descriptor.pathExists("/entry0/dataSD/dataSD")) // IN16B old
71 )) {
72 return 80;
73 } else {
74 return 0;
75 }
76}
77
78//----------------------------------------------------------------------------------------------
82 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, ".nxs"),
83 "File path of the Data file to load");
84
85 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
86 "The name to use for the output workspace");
87
88 std::vector<std::string> loadingOptions{"Spectrometer", "Diffractometer"};
89 declareProperty("LoadDetectors", "Spectrometer", std::make_shared<StringListValidator>(loadingOptions),
90 "Select the type of data to load from IN16B.");
91}
92
93//----------------------------------------------------------------------------------------------
97
98 // Retrieve filename
99 const std::string filenameData = getPropertyValue("Filename");
100
101 m_loadOption = getPropertyValue("LoadDetectors");
102
103 size_t progressSteps = m_loadOption == "Diffractometer" ? 5 : 7;
104 Progress progress(this, 0., 1., progressSteps);
105
106 // open the root node
107 NeXus::NXRoot dataRoot(filenameData);
108 NXEntry firstEntry = dataRoot.openFirstEntry();
109
110 // Load Data details (number of tubes, channels, mode, etc)
111 loadDataDetails(firstEntry);
112 progress.report("Loaded metadata");
113
114 const std::string instrumentPath = LoadHelper::findInstrumentNexusPath(firstEntry);
115 setInstrumentName(firstEntry, instrumentPath);
116
118 progress.report("Initialised the workspace");
119
120 loadNexusEntriesIntoProperties(filenameData);
121 progress.report("Loaded data details");
122
123 if (m_loadOption == "Diffractometer") {
124 loadDiffractionData(firstEntry);
125 } else {
126 loadDataIntoTheWorkSpace(firstEntry);
127 }
128 progress.report("Loaded the data");
129
131 progress.report("Loaded the instrument");
132
133 if (m_loadOption == "Spectrometer") {
134 moveSingleDetectors(firstEntry);
135 progress.report("Loaded the single detectors");
136
137 rotateTubes();
138 progress.report("Rotating tubes if necessary");
139 }
140 // Set the output workspace property
141 setProperty("OutputWorkspace", m_localWorkspace);
142}
143
149void LoadILLIndirect2::setInstrumentName(const NeXus::NXEntry &firstEntry, const std::string &instrumentNamePath) {
150 if (instrumentNamePath.empty()) {
151 std::string message("Cannot set the instrument name from the Nexus file!");
152 g_log.error(message);
153 throw std::runtime_error(message);
154 }
155 m_instrumentName = LoadHelper::getStringFromNexusPath(firstEntry, instrumentNamePath + "/name");
156 boost::to_upper(m_instrumentName); // "IN16b" in file, keep it upper case.
157 g_log.debug() << "Instrument name set to: " + m_instrumentName << '\n';
158}
159
161 NeXus::NXClass instrument = entry.openNXGroup("instrument");
162 if (m_loadOption == "Diffractometer") {
163
164 if (instrument.containsGroup("DiffDet")) {
165 return "instrument/DiffDet/data";
166 } else if (entry.containsGroup("dataDiffDet")) {
167 return "dataDiffDet/DiffDet_data";
168 } else {
169 throw std::runtime_error("Cannot find diffraction detector data in the Nexus file. Make sure "
170 "they exist or load the spectrometer data instead.");
171 }
172 } else {
173 return "data";
174 }
175}
176
182
183 // find the data
184 std::string dataPath = getDataPath(entry);
185
186 // read in the data
187 NXData dataGroup = entry.openNXData(dataPath);
188 NXInt data = dataGroup.openIntData();
189
190 m_numberOfTubes = static_cast<size_t>(data.dim0());
191 m_numberOfPixelsPerTube = static_cast<size_t>(data.dim1());
192 m_numberOfChannels = static_cast<size_t>(data.dim2());
193
194 try {
195 NXInt mode = entry.openNXInt("acquisition_mode");
196 mode.load();
197 m_bats = mode[0] == 1;
198 } catch (...) {
199 g_log.information() << "Unable to read acquisition_mode, assuming doppler";
200 }
201
202 // check which single detectors are enabled, and store their indices
203 if (m_loadOption == "Spectrometer") {
204 NXData dataSDGroup = entry.openNXData("dataSD");
205 NXInt dataSD = dataSDGroup.openIntData();
206
207 for (int i = 1; i <= dataSD.dim0(); ++i) {
208 try {
209 std::string entryNameFlagSD = boost::str(boost::format("instrument/SingleD/tubes%i_function") % i);
210 NXFloat flagSD = entry.openNXFloat(entryNameFlagSD);
211 flagSD.load();
212
213 if (flagSD[0] == 1.0) // is enabled
214 {
215 m_activeSDIndices.insert(i);
216 }
217 } catch (...) {
218 // if the flags are not present in the file (e.g. old format), load all
219 m_activeSDIndices.insert(i);
220 }
221 }
223 g_log.information() << "Number of activated single detectors is: " << m_numberOfSimpleDetectors << std::endl;
224
225 try {
226 NXFloat firstTubeAngle = entry.openNXFloat("instrument/PSD/PSD angle 1");
227 firstTubeAngle.load();
228 m_firstTubeAngleRounded = static_cast<size_t>(std::round(10 * firstTubeAngle[0]));
229 } catch (...) {
231 g_log.information() << "Unable to read first tube angle, assuming 251";
232 }
233 } else {
235 }
236}
237
245 WorkspaceFactory::Instance().create("Workspace2D", nHistograms, m_numberOfChannels + 1, m_numberOfChannels);
246 const auto timeChannels =
247 make_cow<HistogramData::HistogramX>(m_numberOfChannels + 1, HistogramData::LinearGenerator(0.0, 1.0));
248 for (size_t i = 0; i < nHistograms; ++i) {
249 m_localWorkspace->setSharedX(i, timeChannels);
250 }
251 m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("Empty");
252 m_localWorkspace->setYUnitLabel("Counts");
253}
254
260
261 NXData dataGroup = entry.openNXData("data");
262 NXInt data = dataGroup.openIntData();
263 data.load();
264
265 NXData dataSDGroup = entry.openNXData("dataSD");
266 NXInt dataSD = dataSDGroup.openIntData();
267 dataSD.load();
268
269 NXData dataMonGroup = entry.openNXData("monitor/data");
270 NXInt dataMon = dataMonGroup.openIntData();
271 dataMon.load();
272
273 // First, Monitor
274 // Assign Y
275 int *monitor_p = &dataMon(0, 0);
276 m_localWorkspace->dataY(0).assign(monitor_p, monitor_p + m_numberOfChannels);
277 // Assign Error
278 MantidVec &dataE = m_localWorkspace->dataE(0);
279 std::transform(monitor_p, monitor_p + m_numberOfChannels, dataE.begin(), [](const double v) { return std::sqrt(v); });
280 // Then Tubes
282 for (int i = 0; i < static_cast<int>(m_numberOfTubes); ++i) {
283 for (size_t j = 0; j < m_numberOfPixelsPerTube; ++j) {
284 const size_t index = i * m_numberOfPixelsPerTube + j + m_numberOfMonitors;
285 // Assign Y
286 int *data_p = &data(static_cast<int>(i), static_cast<int>(j), 0);
287 m_localWorkspace->dataY(index).assign(data_p, data_p + m_numberOfChannels);
288 // Assign Error
289 MantidVec &E = m_localWorkspace->dataE(index);
290 std::transform(data_p, data_p + m_numberOfChannels, E.begin(), [](const double v) { return std::sqrt(v); });
291 }
292 }
293 // Then add Simple Detector (SD)
295 for (auto &index : m_activeSDIndices) {
296 // Assign Y, note that index starts from 1
297 int *dataSD_p = &dataSD(index - 1, 0, 0);
298 m_localWorkspace->dataY(offset).assign(dataSD_p, dataSD_p + m_numberOfChannels);
299 // Assign Error
300 MantidVec &E = m_localWorkspace->dataE(offset);
301 std::transform(dataSD_p, dataSD_p + m_numberOfChannels, E.begin(), [](const double v) { return std::sqrt(v); });
302 ++offset;
303 }
304}
305
312 NeXus::NXClass instrument = entry.openNXGroup("instrument");
313
314 // first, determine version
315 bool newVersion = instrument.containsDataSet("version");
316
317 // find the path to the data
318 std::string dataPath = getDataPath(entry);
319
320 NXData dataGroup = entry.openNXData(dataPath);
321
322 NXInt data = dataGroup.openIntData();
323 data.load();
324
325 NXData dataMonGroup = entry.openNXData("monitor/data");
326 NXInt dataMon = dataMonGroup.openIntData();
327 dataMon.load();
328
329 // First, Monitor
330 // Assign Y
331 int *monitor_p = &dataMon(0, 0);
332 m_localWorkspace->dataY(0).assign(monitor_p, monitor_p + m_numberOfChannels);
333 // Assign Error
334 MantidVec &dataE = m_localWorkspace->dataE(0);
335 std::transform(monitor_p, monitor_p + m_numberOfChannels, dataE.begin(), [](const double v) { return std::sqrt(v); });
336
338 for (int i = 0; i < static_cast<int>(m_numberOfTubes); ++i) {
339 for (size_t j = 0; j < m_numberOfPixelsPerTube; ++j) {
340 size_t index;
341 if (!newVersion) {
342 // Then Tubes
343 if (i == 2 || i == 3) {
346 } else {
348 }
349 } else {
351 }
352 // Assign Y
353 int *data_p = &data(static_cast<int>(i), static_cast<int>(j), 0);
354 m_localWorkspace->dataY(index).assign(data_p, data_p + m_numberOfChannels);
355 // Assign Error
356 MantidVec &E = m_localWorkspace->dataE(index);
357 std::transform(data_p, data_p + m_numberOfChannels, E.begin(), [](const double v) { return std::sqrt(v); });
358 }
359 }
360}
361
366void LoadILLIndirect2::loadNexusEntriesIntoProperties(const std::string &nexusfilename) {
367
368 API::Run &runDetails = m_localWorkspace->mutableRun();
369 NXhandle nxfileID;
370 NXstatus stat = NXopen(nexusfilename.c_str(), NXACC_READ, &nxfileID);
371 if (stat == NX_ERROR) {
372 g_log.debug() << "convertNexusToProperties: Error loading " << nexusfilename;
373 throw Kernel::Exception::FileError("Unable to open File:", nexusfilename);
374 }
375 LoadHelper::addNexusFieldsToWsRun(nxfileID, runDetails);
376 runDetails.addProperty("Facility", std::string("ILL"));
377 NXclose(&nxfileID);
378}
379
384 auto loadInst = createChildAlgorithm("LoadInstrument");
385 loadInst->setPropertyValue("Filename", getInstrumentFilePath());
386 loadInst->setPropertyValue("InstrumentName", m_instrumentName);
387 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", m_localWorkspace);
388 loadInst->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(true));
389 loadInst->execute();
390}
391
398 Poco::Path directory(ConfigService::Instance().getInstrumentDirectory());
399 std::string idf = m_instrumentName;
400 if (m_loadOption == "Diffractometer") {
401 idf += "D";
402 } else if (!m_bats && m_firstTubeAngleRounded == 251) {
403 // load the instrument with the first tube analyser focused at the midpoint
404 // of sample to tube center
405 idf += "F";
406 }
407 Poco::Path file(idf + "_Definition.xml");
408 Poco::Path fullPath(directory, file);
409 return fullPath.toString();
410}
411
417void LoadILLIndirect2::moveComponent(const std::string &componentName, double twoTheta) {
418 Geometry::Instrument_const_sptr instrument = m_localWorkspace->getInstrument();
419 Geometry::IComponent_const_sptr component = instrument->getComponentByName(componentName);
420 double r, theta, phi;
421 V3D oldPos = component->getPos();
422 oldPos.getSpherical(r, theta, phi);
423 V3D newPos;
424 newPos.spherical(r, twoTheta, phi);
425 g_log.debug() << componentName << " : t = " << theta << " ==> t = " << twoTheta << "\n";
426 auto &compInfo = m_localWorkspace->mutableComponentInfo();
427 const auto componentIndex = compInfo.indexOf(component->getComponentID());
428 compInfo.setPosition(componentIndex, newPos);
429}
430
437 std::string prefix("single_tube_");
438 int index = 1;
439 for (auto i : m_activeSDIndices) {
440 std::string angleEntry = boost::str(boost::format("instrument/SingleD/SD%i angle") % i);
441 NXFloat angleSD = entry.openNXFloat(angleEntry);
442 angleSD.load();
443 g_log.debug("Moving single detector " + std::to_string(i) + " to t=" + std::to_string(angleSD[0]));
444 moveComponent(prefix + std::to_string(index), angleSD[0]);
445 index++;
446 }
447}
448
456 g_log.warning() << "Unexpected first tube angle found: " << m_firstTubeAngleRounded
457 << " degrees. Check your instrument configuration. "
458 "Assuming 25.1 degrees instead.";
459 } else if (m_firstTubeAngleRounded == 331) {
460 auto rotator = this->createChildAlgorithm("RotateInstrumentComponent");
461 rotator->setProperty("Workspace", m_localWorkspace);
462 rotator->setProperty("RelativeRotation", false);
463 rotator->setPropertyValue("ComponentName", "psds");
464 rotator->setProperty("Y", 1.);
465 rotator->setProperty("Angle", -8.);
466 rotator->execute();
467 }
468}
469
470} // namespace Mantid::DataHandling
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Definition: Algorithm.cpp:842
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
@ Load
allowed here which will be passed to the algorithm
Definition: FileProperty.h:52
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition: LogManager.h:79
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
This class stores information regarding an experimental run as a series of log entries.
Definition: Run.h:38
A property class for workspaces.
Loads an ILL IN16B nexus file into a Mantid workspace.
const std::string category() const override
Algorithm's category for identification.
void loadDiffractionData(NeXus::NXEntry &entry)
LoadILLIndirect2::loadDiffractionData Load IN16B diffraction data from the Nexus file when requested.
void moveComponent(const std::string &, double)
Moves the component to the given 2theta.
std::string m_instrumentName
Name of the instrument.
void initWorkSpace()
Creates the workspace and initialises member variables with the corresponding values.
void loadDataIntoTheWorkSpace(NeXus::NXEntry &entry)
Load data found in nexus file in general, indirect mode.
void loadDataDetails(NeXus::NXEntry &entry)
Load Data details (number of tubes, channels, etc)
const std::string name() const override
Algorithm's name for identification.
std::string getDataPath(const NeXus::NXEntry &entry)
void setInstrumentName(const NeXus::NXEntry &firstEntry, const std::string &instrumentNamePath)
Set member variable with the instrument name.
void runLoadInstrument()
Run the Child Algorithm LoadInstrument.
API::MatrixWorkspace_sptr m_localWorkspace
void rotateTubes()
The detector has two positions.
int confidence(Kernel::NexusDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
std::string getInstrumentFilePath()
Makes up the full path of the relevant IDF dependent on first tube angle and mode.
void exec() override
Execute the algorithm.
void init() override
Initialize the algorithm's properties.
void loadNexusEntriesIntoProperties(const std::string &nexusfilename)
Loads the sample logs.
void moveSingleDetectors(const NeXus::NXEntry &entry)
IN16B has a few single detectors that are place around the sample.
Records the filename and the description of failure.
Definition: Exception.h:98
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Defines a wrapper around a file whose internal structure can be accessed using the NeXus API.
bool pathExists(const std::string &path) const
Query if a path exists.
OptionalBool : Tri-state bool.
Definition: OptionalBool.h:25
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class for 3D vectors.
Definition: V3D.h:34
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
Definition: V3D.cpp:57
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition: V3D.cpp:117
The base class for a Nexus class (group).
Definition: NexusClasses.h:487
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
Definition: NexusClasses.h:546
NXClass openNXGroup(const std::string &name) const
Creates and opens an arbitrary (non-standard) class (group).
Definition: NexusClasses.h:529
bool containsGroup(const std::string &query) const
Returns whether an individual group (or group) is present.
bool containsDataSet(const std::string &query) const
Returns whether an individual dataset is present.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
Definition: NexusClasses.h:551
Templated class implementation of NXDataSet.
Definition: NexusClasses.h:203
void load(const int blocksize=1, int i=-1, int j=-1, int k=-1, int l=-1) override
Implementation of the virtual NXDataSet::load(...) method.
Definition: NexusClasses.h:289
int dim2() const
Returns the number of elements along the third dimension.
int dim0() const
Returns the number of elements along the first dimension.
int dim1() const
Returns the number of elements along the second dimension.
Implements NXdata Nexus class.
Definition: NexusClasses.h:795
NXInt openIntData()
Opens data of int type.
Definition: NexusClasses.h:828
Implements NXentry Nexus class.
Definition: NexusClasses.h:898
NXData openNXData(const std::string &name) const
Opens a NXData.
Definition: NexusClasses.h:912
Implements NXroot Nexus class.
Definition: NexusClasses.h:922
NXEntry openFirstEntry()
Open the first NXentry in the file.
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::string getStringFromNexusPath(const Mantid::NeXus::NXEntry &, const std::string &)
Definition: LoadHelper.cpp:49
void addNexusFieldsToWsRun(NXhandle nxfileID, API::Run &runDetails, const std::string &entryName="", bool useFullPath=false)
Add properties from a nexus file to the workspace run.
Definition: LoadHelper.cpp:131
std::string findInstrumentNexusPath(const Mantid::NeXus::NXEntry &)
Finds the path for the instrument name in the nexus file Usually of the form: entry0/<NXinstrument cl...
Definition: LoadHelper.cpp:37
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition: IComponent.h:161
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
Definition: MultiThreaded.h:22
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition: cow_ptr.h:172
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Output
An output workspace.
Definition: Property.h:54