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"
24#include "MantidNexus/NexusFile.h"
25
26#include <algorithm>
27#include <boost/algorithm/string.hpp>
28#include <boost/format.hpp>
29#include <cmath>
30
31namespace Mantid::DataHandling {
32
33using namespace Kernel;
34using namespace API;
35using namespace Nexus;
36
37// Register the algorithm into the AlgorithmFactory
39
40//----------------------------------------------------------------------------------------------
44 : m_numberOfTubes{16}, m_numberOfChannels{1024}, m_numberOfSimpleDetectors{8}, m_numberOfMonitors{1}, m_bats{false},
45 m_firstTubeAngleRounded{251}, m_supportedInstruments{"IN16B"} {}
46
47//----------------------------------------------------------------------------------------------
49const std::string LoadILLIndirect2::name() const { return "LoadILLIndirect"; }
50
52const std::string LoadILLIndirect2::category() const { return "DataHandling\\Nexus;ILL\\Indirect"; }
53
54//----------------------------------------------------------------------------------------------
55
63
64 // fields existent only at the ILL
65 if (descriptor.isEntry("/entry0/wavelength") // ILL
66 && descriptor.isEntry("/entry0/experiment_identifier") // ILL
67 && descriptor.isEntry("/entry0/mode") // ILL
68 && ((descriptor.isEntry("/entry0/instrument/Doppler/mirror_sense") &&
69 descriptor.isEntry("/entry0/dataSD/SingleD_data")) // IN16B new
70 || (descriptor.isEntry("/entry0/instrument/Doppler/doppler_frequency") &&
71 descriptor.isEntry("/entry0/dataSD/dataSD")) // IN16B old
72 )) {
73 return 80;
74 } else {
75 return 0;
76 }
77}
78
79//----------------------------------------------------------------------------------------------
83 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, ".nxs"),
84 "File path of the Data file to load");
85
86 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
87 "The name to use for the output workspace");
88
89 std::vector<std::string> loadingOptions{"Spectrometer", "Diffractometer"};
90 declareProperty("LoadDetectors", "Spectrometer", std::make_shared<StringListValidator>(loadingOptions),
91 "Select the type of data to load from IN16B.");
92}
93
94//----------------------------------------------------------------------------------------------
98
99 // Retrieve filename
100 const std::string filenameData = getPropertyValue("Filename");
101
102 m_loadOption = getPropertyValue("LoadDetectors");
103
104 size_t progressSteps = m_loadOption == "Diffractometer" ? 5 : 7;
105 Progress progress(this, 0., 1., progressSteps);
106
107 // open the root node
108 Nexus::NXRoot dataRoot(filenameData);
109 NXEntry firstEntry = dataRoot.openFirstEntry();
110
111 // Load Data details (number of tubes, channels, mode, etc)
112 loadDataDetails(firstEntry);
113 progress.report("Loaded metadata");
114
115 const std::string instrumentAddress = LoadHelper::findInstrumentNexusAddress(firstEntry);
116 setInstrumentName(firstEntry, instrumentAddress);
117
119 progress.report("Initialised the workspace");
120
121 loadNexusEntriesIntoProperties(filenameData);
122 progress.report("Loaded data details");
123
124 if (m_loadOption == "Diffractometer") {
125 loadDiffractionData(firstEntry);
126 } else {
127 loadDataIntoWorkspace(firstEntry);
128 }
129 progress.report("Loaded the data");
130
132 progress.report("Loaded the instrument");
133
134 if (m_loadOption == "Spectrometer") {
135 moveSingleDetectors(firstEntry);
136 progress.report("Loaded the single detectors");
137
138 rotateTubes();
139 progress.report("Rotating tubes if necessary");
140 }
141 // Set the output workspace property
142 setProperty("OutputWorkspace", m_localWorkspace);
143}
144
150void LoadILLIndirect2::setInstrumentName(const Nexus::NXEntry &firstEntry, const std::string &instrumentNameAddress) {
151 if (instrumentNameAddress.empty()) {
152 std::string message("Cannot set the instrument name from the Nexus file!");
153 g_log.error(message);
154 throw std::runtime_error(message);
155 }
156 m_instrumentName = LoadHelper::getStringFromNexusAddress(firstEntry, instrumentNameAddress + "/name");
157 boost::to_upper(m_instrumentName); // "IN16b" in file, keep it upper case.
158 g_log.debug() << "Instrument name set to: " + m_instrumentName << '\n';
159}
160
162 Nexus::NXClass instrument = entry.openNXGroup("instrument");
163 if (m_loadOption == "Diffractometer") {
164
165 if (instrument.containsGroup("DiffDet")) {
166 return "instrument/DiffDet/data";
167 } else if (entry.containsGroup("dataDiffDet")) {
168 return "dataDiffDet/DiffDet_data";
169 } else {
170 throw std::runtime_error("Cannot find diffraction detector data in the Nexus file. Make sure "
171 "they exist or load the spectrometer data instead.");
172 }
173 } else {
174 return "data";
175 }
176}
177
183
184 // read in the data
185 auto data = LoadHelper::getIntDataset(entry, getDataAddress(entry));
186
187 m_numberOfTubes = static_cast<size_t>(data.dim0());
188 m_numberOfPixelsPerTube = static_cast<size_t>(data.dim1());
189 m_numberOfChannels = static_cast<size_t>(data.dim2());
190
191 try {
192 NXInt mode = entry.openNXInt("acquisition_mode");
193 mode.load();
194 m_bats = mode[0] == 1;
195 } catch (...) {
196 g_log.information() << "Unable to read acquisition_mode, assuming doppler";
197 }
198
199 // check which single detectors are enabled, and store their indices
200 if (m_loadOption == "Spectrometer") {
201 auto dataSD = LoadHelper::getIntDataset(entry, "dataSD");
202
203 for (Nexus::dimsize_t i = 0; i < dataSD.dim0(); ++i) {
204 try {
205 std::string entryNameFlagSD = boost::str(boost::format("instrument/SingleD/tubes%i_function") % (i + 1));
206 NXFloat flagSD = entry.openNXFloat(entryNameFlagSD);
207 flagSD.load();
208
209 if (flagSD[0] == 1.0) // is enabled
210 {
211 m_activeSDIndices.insert(static_cast<detid_t>(i));
212 }
213 } catch (...) {
214 // if the flags are not present in the file (e.g. old format), load all
215 m_activeSDIndices.insert(static_cast<detid_t>(i));
216 }
217 }
219 g_log.information() << "Number of activated single detectors is: " << m_numberOfSimpleDetectors << std::endl;
220
221 try {
222 NXFloat firstTubeAngle = entry.openNXFloat("instrument/PSD/PSD angle 1");
223 firstTubeAngle.load();
224 m_firstTubeAngleRounded = static_cast<size_t>(std::round(10 * firstTubeAngle[0]));
225 } catch (...) {
227 g_log.information() << "Unable to read first tube angle, assuming 251";
228 }
229 } else {
231 }
232}
233
241 WorkspaceFactory::Instance().create("Workspace2D", nHistograms, m_numberOfChannels + 1, m_numberOfChannels);
242 const auto timeChannels =
243 make_cow<HistogramData::HistogramX>(m_numberOfChannels + 1, HistogramData::LinearGenerator(0.0, 1.0));
244 for (size_t i = 0; i < nHistograms; ++i) {
245 m_localWorkspace->setSharedX(i, timeChannels);
246 }
247 m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("Empty");
248 m_localWorkspace->setYUnitLabel("Counts");
249}
250
256
257 // First, let's create common X-axis
258 std::vector<double> xAxis(m_numberOfChannels + 1);
259 std::iota(xAxis.begin(), xAxis.end(), 0.0);
260
261 // Then load monitor
262 auto dataMon = LoadHelper::getIntDataset(entry, "monitor/data");
263 dataMon.load();
265
266 // Then Tubes
267 auto data = LoadHelper::getIntDataset(entry, "data");
268 data.load();
270
271 // And finally Simple Detectors (SD)
272 if (m_activeSDIndices.size() == 0) // there is no point to try to load from empty list of active indices
273 return;
274 int offset = static_cast<int>(m_numberOfTubes * m_numberOfPixelsPerTube + m_numberOfMonitors);
275 auto dataSD = LoadHelper::getIntDataset(entry, "dataSD");
276 dataSD.load();
277 std::set<detid_t> sdIndices;
278 std::transform(m_activeSDIndices.cbegin(), m_activeSDIndices.cend(), std::inserter(sdIndices, sdIndices.begin()),
279 [offset](const auto &index) { return index + offset; });
280 LoadHelper::fillStaticWorkspace(m_localWorkspace, dataSD, xAxis, offset, false, std::vector<int>(), sdIndices);
281}
282
289 Nexus::NXClass instrument = entry.openNXGroup("instrument");
290
291 // first, determine version
292 bool newVersion = instrument.containsDataSet("version");
293
294 auto data = LoadHelper::getIntDataset(entry, getDataAddress(entry));
295 data.load();
296
297 auto dataMon = LoadHelper::getIntDataset(entry, "monitor/data");
298 dataMon.load();
299
300 // First, Monitor
301 // Assign Y
302 int *monitor_p = &dataMon(0, 0);
303 m_localWorkspace->dataY(0).assign(monitor_p, monitor_p + m_numberOfChannels);
304 // Assign Error
305 MantidVec &dataE = m_localWorkspace->dataE(0);
306 std::transform(monitor_p, monitor_p + m_numberOfChannels, dataE.begin(), [](const double v) { return std::sqrt(v); });
307
309 for (int i = 0; i < static_cast<int>(m_numberOfTubes); ++i) {
310 for (size_t j = 0; j < m_numberOfPixelsPerTube; ++j) {
311 size_t index;
312 if (!newVersion) {
313 // Then Tubes
314 if (i == 2 || i == 3) {
317 } else {
319 }
320 } else {
322 }
323 // Assign Y
324 int *data_p = &data(static_cast<int>(i), static_cast<int>(j), 0);
325 m_localWorkspace->dataY(index).assign(data_p, data_p + m_numberOfChannels);
326 // Assign Error
327 MantidVec &E = m_localWorkspace->dataE(index);
328 std::transform(data_p, data_p + m_numberOfChannels, E.begin(), [](const double v) { return std::sqrt(v); });
329 }
330 }
331}
332
337void LoadILLIndirect2::loadNexusEntriesIntoProperties(const std::string &nexusfilename) {
338 API::Run &runDetails = m_localWorkspace->mutableRun();
339
340 try {
341 Nexus::File nxfileID(nexusfilename, NXaccess::READ);
342 LoadHelper::addNexusFieldsToWsRun(nxfileID, runDetails);
343 } catch (Nexus::Exception const &e) {
344 g_log.debug() << "convertNexusToProperties: Error loading \"" << nexusfilename << "\" in read mode: " << e.what()
345 << "\n";
346 throw Kernel::Exception::FileError("Unable to open File:", nexusfilename);
347 }
348 runDetails.addProperty("Facility", std::string("ILL"));
349}
350
356 std::string instrumentFileName = m_instrumentName;
357 if (m_loadOption == "Diffractometer") {
358 instrumentFileName += "D";
359 } else if (!m_bats && m_firstTubeAngleRounded == 251) {
360 // load the instrument with the first tube analyser focused at the midpoint
361 // of sample to tube center
362 instrumentFileName += "F";
363 }
364 instrumentFileName += "_Definition.xml";
365 return instrumentFileName;
366}
367
373void LoadILLIndirect2::moveComponent(const std::string &componentName, double twoTheta) {
374 Geometry::Instrument_const_sptr instrument = m_localWorkspace->getInstrument();
375 Geometry::IComponent_const_sptr component = instrument->getComponentByName(componentName);
376 double r, theta, phi;
377 V3D oldPos = component->getPos();
378 oldPos.getSpherical(r, theta, phi);
379 V3D newPos;
380 newPos.spherical(r, twoTheta, phi);
381 g_log.debug() << componentName << " : t = " << theta << " ==> t = " << twoTheta << "\n";
382 auto &compInfo = m_localWorkspace->mutableComponentInfo();
383 const auto componentIndex = compInfo.indexOf(component->getComponentID());
384 compInfo.setPosition(componentIndex, newPos);
385}
386
393 std::string prefix("single_tube_");
394 int index = 1;
395 for (auto i : m_activeSDIndices) {
396 std::string angleEntry = boost::str(boost::format("instrument/SingleD/SD%i angle") % (i + 1));
397 NXFloat angleSD = entry.openNXFloat(angleEntry);
398 angleSD.load();
399 g_log.debug("Moving single detector " + std::to_string(i) + " to t=" + std::to_string(angleSD[0]));
400 moveComponent(prefix + std::to_string(index), angleSD[0]);
401 index++;
402 }
403}
404
412 g_log.warning() << "Unexpected first tube angle found: " << m_firstTubeAngleRounded
413 << " degrees. Check your instrument configuration. "
414 "Assuming 25.1 degrees instead.";
415 } else if (m_firstTubeAngleRounded == 331) {
416 auto rotator = this->createChildAlgorithm("RotateInstrumentComponent");
417 rotator->setProperty("Workspace", m_localWorkspace);
418 rotator->setProperty("RelativeRotation", false);
419 rotator->setPropertyValue("ComponentName", "psds");
420 rotator->setProperty("Y", 1.);
421 rotator->setProperty("Angle", -8.);
422 rotator->execute();
423 }
424}
425
426} // namespace Mantid::DataHandling
std::map< DeltaEMode::Type, std::string > index
#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.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
@ Load
allowed here which will be passed to the algorithm
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
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:35
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 loadDataIntoWorkspace(const Nexus::NXEntry &entry)
Load data found in nexus file in general, indirect mode.
void setInstrumentName(const Nexus::NXEntry &firstEntry, const std::string &instrumentNameAddress)
Set member variable with the instrument name.
void moveComponent(const std::string &, double)
Moves the component to the given 2theta.
std::string m_instrumentName
Name of the instrument.
std::string getDataAddress(const Nexus::NXEntry &entry)
void initWorkSpace()
Creates the workspace and initialises member variables with the corresponding values.
void loadDiffractionData(Nexus::NXEntry &entry)
LoadILLIndirect2::loadDiffractionData Load IN16B diffraction data from the Nexus file when requested.
const std::string name() const override
Algorithm's name for identification.
std::string getInstrumentFileName()
Attaches proper suffixes of the relevant IDF dependent on first tube angle and mode.
void loadDataDetails(const Nexus::NXEntry &entry)
Load Data details (number of tubes, channels, etc)
API::MatrixWorkspace_sptr m_localWorkspace
void rotateTubes()
The detector has two positions.
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.
int confidence(Nexus::NexusDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
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:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
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:56
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition V3D.cpp:116
Class that provides for a standard Nexus exception.
The base class for a Nexus class (group).
bool containsGroup(const std::string &query) const
Returns whether an individual group (or group) is present.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
NXClass openNXGroup(const std::string &name) const
Creates and opens an arbitrary (non-standard) class (group).
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
bool containsDataSet(const std::string &query) const
Returns whether an individual dataset is present.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
Implements NXentry Nexus class.
Implements NXroot Nexus class.
NXEntry openFirstEntry()
Open the first NXentry in the file.
bool isEntry(const std::string &entryName, const std::string &groupClass) const noexcept
Checks if a full-address entry exists for a particular groupClass in a Nexus dataset.
std::string getStringFromNexusAddress(const Mantid::Nexus::NXEntry &, const std::string &)
void addNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails, const std::string &entryName="", bool useFullAddress=false)
Add properties from a nexus file to the workspace run.
Nexus::NXInt getIntDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXInt data from the requested group name in the entry provided.
void fillStaticWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, bool pointData=false, const std::vector< detid_t > &detectorIDs=std::vector< int >(), const std::set< detid_t > &acceptedID=std::set< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
void loadEmptyInstrument(const API::MatrixWorkspace_sptr &ws, const std::string &instrumentName, const std::string &instrumentAddress="")
Loads empty instrument of chosen name into a provided workspace.
std::string findInstrumentNexusAddress(const Mantid::Nexus::NXEntry &)
Finds the address for the instrument name in the nexus file Usually of the form: entry0/<NXinstrument...
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
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.
int32_t detid_t
Typedef for a detector ID.
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