Mantid
Loading...
Searching...
No Matches
UpdateInstrumentFromFile.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#include "LoadRaw/isisraw2.h"
22#include "MantidNexus/NexusFile.h"
23#include "MantidTypes/SpectrumDefinition.h"
24
25#include <H5Cpp.h>
26#include <algorithm>
27#include <boost/algorithm/string/predicate.hpp>
28#include <boost/algorithm/string/trim.hpp>
29#include <boost/scoped_ptr.hpp>
30#include <fstream>
31#include <set>
32
33namespace Mantid::DataHandling {
34
35DECLARE_ALGORITHM(UpdateInstrumentFromFile)
36
37using namespace Kernel;
38using namespace API;
41using Kernel::V3D;
42
44UpdateInstrumentFromFile::UpdateInstrumentFromFile() : m_workspace(), m_ignorePhi(false), m_ignoreMonitors(true) {}
45
48 // When used as a Child Algorithm the workspace name is not used - hence the
49 // "Anonymous" to satisfy the validator
50 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("Workspace", "Anonymous", Direction::InOut),
51 "The name of the workspace in which to store the imported instrument");
52 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load,
53 std::vector<std::string>{".raw", ".nxs", ".dat", ".s*"}),
54 "The filename of the input file.\n"
55 "Currently supports RAW, ISIS NeXus, DAT & multi-column (at "
56 "least 2) ascii files");
57 declareProperty("MoveMonitors", (!m_ignoreMonitors),
58 "If true the positions of any detectors marked as monitors "
59 "in the IDF will be moved also");
60 declareProperty("IgnorePhi", m_ignorePhi, "If true the phi values form the file will be ignored ");
61 declareProperty("AsciiHeader", "",
62 "If the file is a simple text file, then this property is used to"
63 "define the values in each column of the file. For example: "
64 "spectrum,theta,t0,-,R"
65 "Keywords=spectrum,ID,R,theta,phi. A dash means skip column. Keywords "
66 "are recognised"
67 "as identifying components to move to new positions. Any other names in "
68 "the list"
69 "are added as instrument parameters.");
70 declareProperty("SkipFirstNLines", 0,
71 "If the file is ASCII, then skip this "
72 "number of lines at the start of the "
73 "file");
74}
75
82 // Retrieve the filename from the properties
83 const std::string filename = getPropertyValue("Filename");
84 m_workspace = getProperty("Workspace");
85
86 if (!m_workspace->getInstrument()) {
87 throw std::runtime_error("Input workspace has no defined instrument");
88 }
89
90 m_ignorePhi = getProperty("IgnorePhi");
91 const bool moveMonitors = getProperty("MoveMonitors");
92 m_ignoreMonitors = (!moveMonitors);
93
94 // Check file type
95 if (H5::H5File::isHdf5(filename)) {
96 LoadISISNexus2 isisNexus;
97 LoadEventNexus eventNexus;
98
99 // we open and close the HDF5 file.
100 boost::scoped_ptr<Nexus::NexusDescriptorLazy> descriptorNexusLazy(new Nexus::NexusDescriptorLazy(filename));
101
102 if (isisNexus.confidence(*descriptorNexusLazy) > 0 || eventNexus.confidence(*descriptorNexusLazy) > 0) {
103 const auto &rootEntry = descriptorNexusLazy->firstEntryNameType();
104 Nexus::File nxFile(filename);
105 nxFile.openGroup(rootEntry.first, rootEntry.second);
106 updateFromNeXus(nxFile);
107 return;
108 }
109 }
110
111 if (FileDescriptor::isAscii(filename)) {
112 // If no header specified & the extension is .dat or .sca, then assume ISIS
113 // DAT file structure
114 if (getPropertyValue("AsciiHeader").empty() &&
115 (boost::iends_with(filename, ".dat") || boost::iends_with(filename, ".sca"))) {
116 this->setPropertyValue("AsciiHeader", "ID,-,R,-,theta,phi,-,-,-,-,-,-,-,-,-,-,-,-,-");
117 this->setProperty("SkipFirstNLines", 2);
118 }
119 updateFromAscii(filename);
120 return;
121 }
122
123 LoadRawHelper isisRAW;
124 auto descriptor = std::make_unique<Kernel::FileDescriptor>(filename);
125 if (isisRAW.confidence(*descriptor) > 0) {
126 updateFromRaw(filename);
127 } else {
128 throw std::invalid_argument("File \"" + filename + "\" is not a valid input file.");
129 }
130}
131
136void UpdateInstrumentFromFile::updateFromRaw(const std::string &filename) {
137 ISISRAW2 iraw;
138 if (iraw.readFromFile(filename.c_str(), false) != 0) {
139 g_log.error("Unable to open file " + filename);
140 throw Exception::FileError("Unable to open File:", filename);
141 }
142
143 const int32_t numDetector = iraw.i_det;
144 std::vector<int32_t> detID(iraw.udet, iraw.udet + numDetector);
145 std::vector<float> l2(iraw.len2, iraw.len2 + numDetector);
146 std::vector<float> theta(iraw.tthe, iraw.tthe + numDetector);
147 // Is ut01 (=phi) present? Sometimes an array is present but has wrong values
148 // e.g.all 1.0 or all 2.0
149 bool phiPresent = iraw.i_use > 0 && iraw.ut[0] != 1.0 && iraw.ut[0] != 2.0;
150 std::vector<float> phi(0);
151 if (phiPresent) {
152 phi = std::vector<float>(iraw.ut, iraw.ut + numDetector);
153 } else {
154 phi = std::vector<float>(numDetector, 0.0);
155 }
156 g_log.information() << "Setting detector postions from RAW file.\n";
157 setDetectorPositions(detID, l2, theta, phi);
158}
159
165 try {
166 nxFile.openGroup("isis_vms_compat", "IXvms");
167 } catch (Nexus::Exception const &) {
168 throw std::runtime_error("Unknown NeXus flavour. Cannot update instrument "
169 "positions using this type of file");
170 }
171 // Det ID
172 std::vector<int32_t> detID;
173 nxFile.openData("UDET");
174 nxFile.getData(detID);
175 nxFile.closeData();
176 // Position information
177 std::vector<float> l2, theta, phi;
178 nxFile.openData("LEN2");
179 nxFile.getData(l2);
180 nxFile.closeData();
181 nxFile.openData("TTHE");
182 nxFile.getData(theta);
183 nxFile.closeData();
184 nxFile.openData("UT01");
185 nxFile.getData(phi);
186 nxFile.closeData();
187
188 g_log.information() << "Setting detector postions from NeXus file.\n";
189 setDetectorPositions(detID, l2, theta, phi);
190}
191
196void UpdateInstrumentFromFile::updateFromAscii(const std::string &filename) {
197 AsciiFileHeader header;
198 const bool isSpectrum = parseAsciiHeader(header);
199
200 // Throws for multiple detectors
201 const spec2index_map specToIndex(m_workspace->getSpectrumToWorkspaceIndexMap());
202
203 std::ifstream datfile(filename.c_str(), std::ios_base::in);
204 const int skipNLines = getProperty("SkipFirstNLines");
205 std::string line;
206 int lineCount(0);
207 while (lineCount < skipNLines) {
208 std::getline(datfile, line);
209 ++lineCount;
210 }
211
212 auto &detectorInfo = m_workspace->mutableDetectorInfo();
213 auto &componentInfo = m_workspace->mutableComponentInfo();
214 const auto &spectrumInfo = m_workspace->spectrumInfo();
215
216 std::vector<double> colValues(header.colCount - 1, 0.0);
217 while (std::getline(datfile, line)) {
218 boost::trim(line);
219 std::istringstream is(line);
220 // Column 0 should be ID/spectrum number
221 int32_t detOrSpec(-1000);
222 is >> detOrSpec;
223 // If first thing read is not a number then skip the line
224 if (is.fail()) {
225 g_log.debug() << "Skipping \"" << line << "\". Cannot interpret as list of numbers.\n";
226 continue;
227 }
228
229 bool skip{false};
230 auto index = static_cast<size_t>(-1);
231 if (isSpectrum) {
232 auto it = specToIndex.find(detOrSpec);
233 if (it != specToIndex.end()) {
234 index = it->second;
235 if (!spectrumInfo.hasDetectors(index)) {
236 skip = true;
237 }
238 } else {
239 g_log.debug() << "Skipping \"" << line << "\". Spectrum is not in workspace.\n";
240 continue;
241 }
242 } else {
243 try {
244 index = detectorInfo.indexOf(detOrSpec);
245 } catch (std::out_of_range &) {
246 skip = true;
247 }
248 }
249 if (skip || index == static_cast<size_t>(-1)) {
250 g_log.debug() << "Skipping \"" << line << "\". Spectrum in workspace but cannot find associated detector.\n";
251 continue;
252 }
253
254 std::vector<size_t> indices;
255 if (isSpectrum) {
256 // all (unique) detectors of the spectrum
257 std::set<size_t> detIndices;
258 for (const auto &specDefItem : spectrumInfo.spectrumDefinition(index))
259 detIndices.insert(specDefItem.first);
260 indices.assign(detIndices.cbegin(), detIndices.cend());
261 } else {
262 indices.emplace_back(index);
263 }
264
265 // Special cases for detector r,t,p. Everything else is
266 // attached as an detector parameter
267 double R(0.0), theta(0.0), phi(0.0);
268 for (size_t i = 1; i < header.colCount; ++i) {
269 double value(0.0);
270 is >> value;
271 if (i < header.colCount - 1 && is.eof()) {
272 // If stringstream is at EOF & we are not at the last column then
273 // there aren't enought columns in the file
274 throw std::runtime_error("UpdateInstrumentFromFile::updateFromAscii - "
275 "File contains fewer than expected number of "
276 "columns, check AsciiHeader property.");
277 }
278
279 if (i == header.rColIdx)
280 R = value;
281 else if (i == header.thetaColIdx)
282 theta = value;
283 else if (i == header.phiColIdx)
284 phi = value;
285 else if (header.detParCols.count(i) == 1) {
286 for (const auto detIndex : indices) {
287 componentInfo.addDouble(detIndex, header.colToName[i], value);
288 }
289 }
290 }
291 // Check stream state. stringstream::EOF should have been reached, if not
292 // then there is still more to
293 // read and the file has more columns than the header indicated
294 if (!is.eof()) {
295 throw std::runtime_error("UpdateInstrumentFromFile::updateFromAscii - "
296 "File contains more than expected number of "
297 "columns, check AsciiHeader property.");
298 }
299
300 // If not supplied use current values
301 double r, t, p;
302 if (isSpectrum)
303 spectrumInfo.position(index).getSpherical(r, t, p);
304 else
305 detectorInfo.position(index).getSpherical(r, t, p);
306 if (header.rColIdx == 0)
307 R = r;
308 if (header.thetaColIdx == 0)
309 theta = t;
310 if (header.phiColIdx == 0 || m_ignorePhi)
311 phi = p;
312
313 for (const auto detIndex : indices)
314 setDetectorPosition(detectorInfo, detIndex, static_cast<float>(R), static_cast<float>(theta),
315 static_cast<float>(phi));
316 }
317}
318
327 const std::string header = getProperty("AsciiHeader");
328 if (header.empty()) {
329 throw std::invalid_argument("Ascii file provided but the AsciiHeader "
330 "property is empty, cannot interpret columns");
331 }
332
334 headerInfo.colCount = splitter.count();
335 auto it = splitter.begin(); // First column must be spectrum number or detector ID
336 const std::string &col0 = *it;
337 bool isSpectrum(false);
338 if (boost::iequals("spectrum", col0))
339 isSpectrum = true;
340 if (!isSpectrum && !boost::iequals("id", col0)) {
341 throw std::invalid_argument("Invalid AsciiHeader, first column name must "
342 "be either 'spectrum' or 'id'");
343 }
344
345 ++it;
346 size_t counter(1);
347 for (; it != splitter.end(); ++it) {
348 const std::string &colName = *it;
349 if (boost::iequals("R", colName))
350 headerInfo.rColIdx = counter;
351 else if (boost::iequals("theta", colName))
352 headerInfo.thetaColIdx = counter;
353 else if (boost::iequals("phi", colName))
354 headerInfo.phiColIdx = counter;
355 else if (boost::iequals("-", colName)) // Skip dashed
356 {
357 ++counter;
358 continue;
359 } else {
360 headerInfo.detParCols.insert(counter);
361 headerInfo.colToName.emplace(counter, colName);
362 }
363 ++counter;
364 }
365
366 return isSpectrum;
367}
368
376void UpdateInstrumentFromFile::setDetectorPositions(const std::vector<int32_t> &detID, const std::vector<float> &l2,
377 const std::vector<float> &theta, const std::vector<float> &phi) {
378 const auto numDetector = static_cast<int>(detID.size());
379 g_log.information() << "Setting new positions for " << numDetector << " detectors\n";
380
381 auto &detectorInfo = m_workspace->mutableDetectorInfo();
382 for (int i = 0; i < numDetector; ++i) {
383 try {
384 auto index = detectorInfo.indexOf(detID[i]);
385 double p{phi[i]};
386 if (m_ignorePhi) {
387 double r, t;
388 detectorInfo.position(index).getSpherical(r, t, p);
389 }
390 setDetectorPosition(detectorInfo, index, l2[i], theta[i], static_cast<float>(p));
391 } catch (std::out_of_range &) {
392 // Invalid detID[i]
393 continue;
394 }
395 progress(static_cast<double>(i) / numDetector, "Updating Detector Positions from File");
396 }
397}
398
408 const float l2, const float theta, const float phi) {
409 if (m_ignoreMonitors && detectorInfo.isMonitor(index))
410 return;
411
412 Kernel::V3D pos;
413 pos.spherical(l2, theta, phi);
414 detectorInfo.setPosition(index, pos);
415}
416
417} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
isis raw file.
Definition isisraw2.h:13
float * ut
nuse UT* user tables (total size NUSE*NDET) ut01=phi
Definition isisraw.h:302
int i_use
number of user defined UTn tables NUSE
Definition isisraw.h:293
int i_det
number of detectors NDET
Definition isisraw.h:291
int readFromFile(const char *filename, bool read_data=true)
stuff
Definition isisraw.cpp:927
int * udet
user detector number for each detector (size NDET)
Definition isisraw.h:317
float * tthe
2theta scattering angle (size NDET)
Definition isisraw.h:301
float * len2
L2 table (size NDET)
Definition isisraw.h:299
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.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:426
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
@ Load
allowed here which will be passed to the algorithm
A property class for workspaces.
int confidence(Nexus::NexusDescriptorLazy &descriptor) const override
Return the confidence with with this algorithm can load the file.
Loads a file in a NeXus format and stores it in a 2D workspace.
int confidence(Nexus::NexusDescriptorLazy &descriptor) const override
Returns a confidence value that this algorithm can load a file.
Helper class for LoadRaw algorithms.
int confidence(Kernel::FileDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
void setDetectorPositions(const std::vector< int32_t > &detID, const std::vector< float > &l2, const std::vector< float > &theta, const std::vector< float > &phi)
Set the new detector positions.
void updateFromRaw(const std::string &filename)
Assumes the file is a raw file.
void updateFromAscii(const std::string &filename)
Updates from a more generic ascii file.
void setDetectorPosition(Geometry::DetectorInfo &detectorInfo, const size_t index, const float l2, const float theta, const float phi)
Set the new detector position for a single det ID.
API::MatrixWorkspace_sptr m_workspace
The input workspace to modify.
bool parseAsciiHeader(AsciiFileHeader &headerInfo)
Parse the header and fill the headerInfo struct.
void init() override
Overwrites Algorithm method. Does nothing at present.
void updateFromNeXus(Nexus::File &nxFile)
Assumes the file is an ISIS NeXus file.
void exec() override
Overwrites Algorithm method.
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
void setPosition(const size_t index, const Kernel::V3D &position)
Set the absolute position of the detector with given index. Not thread safe.
bool isMonitor(const size_t index) const
Returns true if the detector is a monitor.
Records the filename and the description of failure.
Definition Exception.h:98
bool isAscii() const
Returns true if the descriptor is looking at an ascii file.
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 information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Iterator begin()
Iterator referring to first element in the container.
@ TOK_TRIM
remove leading and trailing whitespace from tokens
Iterator end()
Iterator referring to the past-the-end element in the container.
std::size_t count() const
Get the total number of tokens.
Class for 3D vectors.
Definition V3D.h:35
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
Definition V3D.cpp:56
Class that provides for a standard Nexus exception.
std::shared_ptr< Instrument > Instrument_sptr
Shared pointer to an instrument object.
std::shared_ptr< Mantid::Geometry::IDetector > IDetector_sptr
Shared pointer to IDetector.
Definition IDetector.h:94
std::unordered_map< specnum_t, size_t > spec2index_map
Map with key = spectrum number, value = workspace index.
Simple structure to store information about the ASCII file header.
@ InOut
Both an input & output workspace.
Definition Property.h:56