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"
21#include "MantidNexus/NexusFile.h"
22
23#include <H5Cpp.h>
24#include <algorithm>
25#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/trim.hpp>
27#include <boost/scoped_ptr.hpp>
28#include <fstream>
29
30namespace Mantid::DataHandling {
31
32DECLARE_ALGORITHM(UpdateInstrumentFromFile)
33
34using namespace Kernel;
35using namespace API;
38using Kernel::V3D;
39
41UpdateInstrumentFromFile::UpdateInstrumentFromFile() : m_workspace(), m_ignorePhi(false), m_ignoreMonitors(true) {}
42
45 // When used as a Child Algorithm the workspace name is not used - hence the
46 // "Anonymous" to satisfy the validator
47 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("Workspace", "Anonymous", Direction::InOut),
48 "The name of the workspace in which to store the imported instrument");
49 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load,
50 std::vector<std::string>{".raw", ".nxs", ".dat", ".s*"}),
51 "The filename of the input file.\n"
52 "Currently supports RAW, ISIS NeXus, DAT & multi-column (at "
53 "least 2) ascii files");
54 declareProperty("MoveMonitors", (!m_ignoreMonitors),
55 "If true the positions of any detectors marked as monitors "
56 "in the IDF will be moved also");
57 declareProperty("IgnorePhi", m_ignorePhi, "If true the phi values form the file will be ignored ");
58 declareProperty("AsciiHeader", "",
59 "If the file is a simple text file, then this property is used to"
60 "define the values in each column of the file. For example: "
61 "spectrum,theta,t0,-,R"
62 "Keywords=spectrum,ID,R,theta,phi. A dash means skip column. Keywords "
63 "are recognised"
64 "as identifying components to move to new positions. Any other names in "
65 "the list"
66 "are added as instrument parameters.");
67 declareProperty("SkipFirstNLines", 0,
68 "If the file is ASCII, then skip this "
69 "number of lines at the start of the "
70 "file");
71}
72
79 // Retrieve the filename from the properties
80 const std::string filename = getPropertyValue("Filename");
81 m_workspace = getProperty("Workspace");
82
83 if (!m_workspace->getInstrument()) {
84 throw std::runtime_error("Input workspace has no defined instrument");
85 }
86
87 m_ignorePhi = getProperty("IgnorePhi");
88 const bool moveMonitors = getProperty("MoveMonitors");
89 m_ignoreMonitors = (!moveMonitors);
90
91 // Check file type
92 if (H5::H5File::isHdf5(filename)) {
93 LoadISISNexus2 isisNexus;
94 LoadEventNexus eventNexus;
95
96 // we open and close the HDF5 file.
97 boost::scoped_ptr<Nexus::NexusDescriptor> descriptorNexusHDF5(new Nexus::NexusDescriptor(filename));
98
99 if (isisNexus.confidence(*descriptorNexusHDF5) > 0 || eventNexus.confidence(*descriptorNexusHDF5) > 0) {
100 const auto &rootEntry = descriptorNexusHDF5->firstEntryNameType();
101 Nexus::File nxFile(filename);
102 nxFile.openGroup(rootEntry.first, rootEntry.second);
103 updateFromNeXus(nxFile);
104 return;
105 }
106 }
107
108 if (FileDescriptor::isAscii(filename)) {
109 // If no header specified & the extension is .dat or .sca, then assume ISIS
110 // DAT file structure
111 if (getPropertyValue("AsciiHeader").empty() &&
112 (boost::iends_with(filename, ".dat") || boost::iends_with(filename, ".sca"))) {
113 this->setPropertyValue("AsciiHeader", "ID,-,R,-,theta,phi,-,-,-,-,-,-,-,-,-,-,-,-,-");
114 this->setProperty("SkipFirstNLines", 2);
115 }
116 updateFromAscii(filename);
117 return;
118 }
119
120 LoadRawHelper isisRAW;
121 auto descriptor = std::make_unique<Kernel::FileDescriptor>(filename);
122 if (isisRAW.confidence(*descriptor) > 0) {
123 updateFromRaw(filename);
124 } else {
125 throw std::invalid_argument("File \"" + filename + "\" is not a valid input file.");
126 }
127}
128
133void UpdateInstrumentFromFile::updateFromRaw(const std::string &filename) {
134 ISISRAW2 iraw;
135 if (iraw.readFromFile(filename.c_str(), false) != 0) {
136 g_log.error("Unable to open file " + filename);
137 throw Exception::FileError("Unable to open File:", filename);
138 }
139
140 const int32_t numDetector = iraw.i_det;
141 std::vector<int32_t> detID(iraw.udet, iraw.udet + numDetector);
142 std::vector<float> l2(iraw.len2, iraw.len2 + numDetector);
143 std::vector<float> theta(iraw.tthe, iraw.tthe + numDetector);
144 // Is ut01 (=phi) present? Sometimes an array is present but has wrong values
145 // e.g.all 1.0 or all 2.0
146 bool phiPresent = iraw.i_use > 0 && iraw.ut[0] != 1.0 && iraw.ut[0] != 2.0;
147 std::vector<float> phi(0);
148 if (phiPresent) {
149 phi = std::vector<float>(iraw.ut, iraw.ut + numDetector);
150 } else {
151 phi = std::vector<float>(numDetector, 0.0);
152 }
153 g_log.information() << "Setting detector postions from RAW file.\n";
154 setDetectorPositions(detID, l2, theta, phi);
155}
156
162 try {
163 nxFile.openGroup("isis_vms_compat", "IXvms");
164 } catch (Nexus::Exception const &) {
165 throw std::runtime_error("Unknown NeXus flavour. Cannot update instrument "
166 "positions using this type of file");
167 }
168 // Det ID
169 std::vector<int32_t> detID;
170 nxFile.openData("UDET");
171 nxFile.getData(detID);
172 nxFile.closeData();
173 // Position information
174 std::vector<float> l2, theta, phi;
175 nxFile.openData("LEN2");
176 nxFile.getData(l2);
177 nxFile.closeData();
178 nxFile.openData("TTHE");
179 nxFile.getData(theta);
180 nxFile.closeData();
181 nxFile.openData("UT01");
182 nxFile.getData(phi);
183 nxFile.closeData();
184
185 g_log.information() << "Setting detector postions from NeXus file.\n";
186 setDetectorPositions(detID, l2, theta, phi);
187}
188
193void UpdateInstrumentFromFile::updateFromAscii(const std::string &filename) {
194 AsciiFileHeader header;
195 const bool isSpectrum = parseAsciiHeader(header);
196
197 // Throws for multiple detectors
198 const spec2index_map specToIndex(m_workspace->getSpectrumToWorkspaceIndexMap());
199
200 std::ifstream datfile(filename.c_str(), std::ios_base::in);
201 const int skipNLines = getProperty("SkipFirstNLines");
202 std::string line;
203 int lineCount(0);
204 while (lineCount < skipNLines) {
205 std::getline(datfile, line);
206 ++lineCount;
207 }
208
209 Geometry::ParameterMap &pmap = m_workspace->instrumentParameters();
210 auto &detectorInfo = m_workspace->mutableDetectorInfo();
211 const auto &spectrumInfo = m_workspace->spectrumInfo();
212
213 std::vector<double> colValues(header.colCount - 1, 0.0);
214 while (std::getline(datfile, line)) {
215 boost::trim(line);
216 std::istringstream is(line);
217 // Column 0 should be ID/spectrum number
218 int32_t detOrSpec(-1000);
219 is >> detOrSpec;
220 // If first thing read is not a number then skip the line
221 if (is.fail()) {
222 g_log.debug() << "Skipping \"" << line << "\". Cannot interpret as list of numbers.\n";
223 continue;
224 }
225
226 bool skip{false};
227 auto index = static_cast<size_t>(-1);
228 const Geometry::IDetector *det{nullptr};
229 if (isSpectrum) {
230 auto it = specToIndex.find(detOrSpec);
231 if (it != specToIndex.end()) {
232 index = it->second;
233 if (spectrumInfo.hasDetectors(index)) {
234 det = &spectrumInfo.detector(index);
235 } else {
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 det = &detectorInfo.detector(index);
246 } catch (std::out_of_range &) {
247 skip = true;
248 }
249 }
250 if (skip || index == static_cast<size_t>(-1)) {
251 g_log.debug() << "Skipping \"" << line << "\". Spectrum in workspace but cannot find associated detector.\n";
252 continue;
253 }
254
255 std::vector<size_t> indices;
256 if (isSpectrum) {
257 if (auto group = dynamic_cast<const Geometry::DetectorGroup *>(det)) {
258 const auto detIDs = group->getDetectorIDs();
259 std::transform(detIDs.cbegin(), detIDs.cend(), std::back_inserter(indices),
260 [detectorInfo](const auto detID) { return detectorInfo.indexOf(detID); });
261 } else {
262 indices.emplace_back(detectorInfo.indexOf(det->getID()));
263 }
264 } else {
265 indices.emplace_back(index);
266 }
267
268 // Special cases for detector r,t,p. Everything else is
269 // attached as an detector parameter
270 double R(0.0), theta(0.0), phi(0.0);
271 for (size_t i = 1; i < header.colCount; ++i) {
272 double value(0.0);
273 is >> value;
274 if (i < header.colCount - 1 && is.eof()) {
275 // If stringstream is at EOF & we are not at the last column then
276 // there aren't enought columns in the file
277 throw std::runtime_error("UpdateInstrumentFromFile::updateFromAscii - "
278 "File contains fewer than expected number of "
279 "columns, check AsciiHeader property.");
280 }
281
282 if (i == header.rColIdx)
283 R = value;
284 else if (i == header.thetaColIdx)
285 theta = value;
286 else if (i == header.phiColIdx)
287 phi = value;
288 else if (header.detParCols.count(i) == 1) {
289 for (const auto detIndex : indices) {
290 auto id = detectorInfo.detector(detIndex).getComponentID();
291 pmap.addDouble(id, header.colToName[i], value);
292 }
293 }
294 }
295 // Check stream state. stringstream::EOF should have been reached, if not
296 // then there is still more to
297 // read and the file has more columns than the header indicated
298 if (!is.eof()) {
299 throw std::runtime_error("UpdateInstrumentFromFile::updateFromAscii - "
300 "File contains more than expected number of "
301 "columns, check AsciiHeader property.");
302 }
303
304 // If not supplied use current values
305 double r, t, p;
306 if (isSpectrum)
307 spectrumInfo.position(index).getSpherical(r, t, p);
308 else
309 detectorInfo.position(index).getSpherical(r, t, p);
310 if (header.rColIdx == 0)
311 R = r;
312 if (header.thetaColIdx == 0)
313 theta = t;
314 if (header.phiColIdx == 0 || m_ignorePhi)
315 phi = p;
316
317 for (const auto detIndex : indices)
318 setDetectorPosition(detectorInfo, detIndex, static_cast<float>(R), static_cast<float>(theta),
319 static_cast<float>(phi));
320 }
321}
322
331 const std::string header = getProperty("AsciiHeader");
332 if (header.empty()) {
333 throw std::invalid_argument("Ascii file provided but the AsciiHeader "
334 "property is empty, cannot interpret columns");
335 }
336
338 headerInfo.colCount = splitter.count();
339 auto it = splitter.begin(); // First column must be spectrum number or detector ID
340 const std::string &col0 = *it;
341 bool isSpectrum(false);
342 if (boost::iequals("spectrum", col0))
343 isSpectrum = true;
344 if (!isSpectrum && !boost::iequals("id", col0)) {
345 throw std::invalid_argument("Invalid AsciiHeader, first column name must "
346 "be either 'spectrum' or 'id'");
347 }
348
349 ++it;
350 size_t counter(1);
351 for (; it != splitter.end(); ++it) {
352 const std::string &colName = *it;
353 if (boost::iequals("R", colName))
354 headerInfo.rColIdx = counter;
355 else if (boost::iequals("theta", colName))
356 headerInfo.thetaColIdx = counter;
357 else if (boost::iequals("phi", colName))
358 headerInfo.phiColIdx = counter;
359 else if (boost::iequals("-", colName)) // Skip dashed
360 {
361 ++counter;
362 continue;
363 } else {
364 headerInfo.detParCols.insert(counter);
365 headerInfo.colToName.emplace(counter, colName);
366 }
367 ++counter;
368 }
369
370 return isSpectrum;
371}
372
380void UpdateInstrumentFromFile::setDetectorPositions(const std::vector<int32_t> &detID, const std::vector<float> &l2,
381 const std::vector<float> &theta, const std::vector<float> &phi) {
382 const auto numDetector = static_cast<int>(detID.size());
383 g_log.information() << "Setting new positions for " << numDetector << " detectors\n";
384
385 auto &detectorInfo = m_workspace->mutableDetectorInfo();
386 for (int i = 0; i < numDetector; ++i) {
387 try {
388 auto index = detectorInfo.indexOf(detID[i]);
389 double p{phi[i]};
390 if (m_ignorePhi) {
391 double r, t;
392 detectorInfo.position(index).getSpherical(r, t, p);
393 }
394 setDetectorPosition(detectorInfo, index, l2[i], theta[i], static_cast<float>(p));
395 } catch (std::out_of_range &) {
396 // Invalid detID[i]
397 continue;
398 }
399 progress(static_cast<double>(i) / numDetector, "Updating Detector Positions from File");
400 }
401}
402
412 const float l2, const float theta, const float phi) {
413 if (m_ignoreMonitors && detectorInfo.isMonitor(index))
414 return;
415
416 Kernel::V3D pos;
417 pos.spherical(l2, theta, phi);
418 detectorInfo.setPosition(index, pos);
419}
420
421} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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:422
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::NexusDescriptor &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::NexusDescriptor &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.
Holds a collection of detectors.
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.
Interface class for detector objects.
Definition IDetector.h:43
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: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
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:100
std::unordered_map< specnum_t, size_t > spec2index_map
Map with key = spectrum number, value = workspace index.
Generate a tableworkspace to store the calibration results.
Simple structure to store information about the ASCII file header.
@ InOut
Both an input & output workspace.
Definition Property.h:55