Mantid
Loading...
Searching...
No Matches
LoadGSS.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 +
7//---------------------------------------------------
8// Includes
9//---------------------------------------------------
11#include "MantidAPI/Axis.h"
13#include "MantidAPI/ISpectrum.h"
22
23#include <boost/regex.hpp>
24#include <fstream>
25#include <sstream>
26#include <string>
27
28using namespace Mantid::DataHandling;
29using namespace Mantid::API;
30using namespace Mantid::HistogramData;
31using namespace Mantid::Kernel;
32
33namespace Mantid::DataHandling {
34
36
37namespace { // anonymous namespace
38const boost::regex DET_POS_REG_EXP{"^#.+flight path\\s+([0-9.]+).+"
39 "tth\\s+([0-9.]+).+"
40 "DIFC\\s+([0-9.]+)"};
41const boost::regex L1_REG_EXP{"^#.+flight path\\s+([0-9.]+)\\s*m"};
42} // end of anonymous namespace
43
44//----------------------------------------------------------------------------------------------
51
52 if (!descriptor.isAscii() || descriptor.extension() == ".tar")
53 return 0;
54
55 std::string str;
56 std::istream &file = descriptor.data();
57 std::getline(file, str); // workspace title first line
58 while (!file.eof()) {
59 std::getline(file, str);
60 // Skip over empty and comment lines, as well as those coming from files
61 // saved with the 'ExtendedHeader' option
62 if (str.empty() || str[0] == '#' || str.compare(0, 8, "Monitor:") == 0) {
63 continue;
64 }
65 if (str.compare(0, 4, "BANK") == 0 &&
66 (str.find("RALF") != std::string::npos || str.find("SLOG") != std::string::npos) &&
67 (str.find("FXYE") != std::string::npos)) {
68 return 80;
69 }
70 }
71 return 0;
72}
73
74//----------------------------------------------------------------------------------------------
78 const std::vector<std::string> exts{".gsa", ".gss", ".gda", ".txt"};
79 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Load, exts),
80 "The input filename of the stored data");
81
82 declareProperty(std::make_unique<API::WorkspaceProperty<>>("OutputWorkspace", "", Kernel::Direction::Output),
83 "Workspace name to load into.");
84
85 declareProperty("UseBankIDasSpectrumNumber", false,
86 "If true, spectrum number corresponding to each bank is to "
87 "be its bank ID. ");
88}
89
90//----------------------------------------------------------------------------------------------
94 // Process input parameters
95 std::string filename = getPropertyValue("Filename");
96
97 bool useBankAsSpectrum = getProperty("UseBankIDasSpectrumNumber");
98
99 MatrixWorkspace_sptr outputWorkspace = loadGSASFile(filename, useBankAsSpectrum);
100
101 setProperty("OutputWorkspace", outputWorkspace);
102}
103
104//----------------------------------------------------------------------------------------------
107API::MatrixWorkspace_sptr LoadGSS::loadGSASFile(const std::string &filename, bool useBankAsSpectrum) {
108 // Vectors for detector information
109 double primaryflightpath = -1;
110 std::vector<double> twothetas;
111 std::vector<double> difcs;
112 std::vector<double> totalflightpaths;
113 std::vector<int> detectorIDs;
114
115 // Vectors to store data
116 std::vector<HistogramData::BinEdges> gsasDataX;
117 std::vector<HistogramData::Counts> gsasDataY;
118 std::vector<HistogramData::CountStandardDeviations> gsasDataE;
119
120 std::vector<double> vecX, vecY, vecE;
121
122 // progress
123 std::unique_ptr<Progress> prog = nullptr;
124
125 // Parameters for reading file
126 char currentLine[256];
127 std::string wsTitle;
128 std::string slogTitle;
129 std::string instrumentname = "Generic";
130 char filetype = 'x';
131
132 // Gather data
133 std::ifstream input(filename.c_str(), std::ios_base::in);
134 if (!input.is_open()) {
135 // throw exception if file cannot be opened
136 std::stringstream errss;
137 errss << "Unable to open GSAS file " << filename;
138 throw std::runtime_error(errss.str());
139 }
140
141 // First line: Title
142 if (!input.eof()) {
143 // Get workspace title (should be first line or 2nd line for SLOG)
144 input.getline(currentLine, 256);
145 wsTitle = currentLine;
146 } else {
147 throw std::runtime_error("File is empty");
148 }
149
150 // Loop all the lines
151 bool isOutOfHead = false;
152 bool slogtitleset = false;
153 bool multiplybybinwidth = false;
154 int nSpec = 0;
155 bool calslogx0 = true;
156 double bc4 = 0;
157 double bc3 = 0;
158
159 while (!input.eof() && input.getline(currentLine, 256)) {
160 // Initialize progress after NSpec is imported
161 if (nSpec != 0 && !prog) {
162 prog = std::make_unique<Progress>(this, 0.0, 1.0, nSpec);
163 }
164
165 // Set flag to test SLOG
166 if (!slogtitleset) {
167 slogTitle = currentLine;
168 slogtitleset = true;
169 }
170
171 if (currentLine[0] == '\n' || currentLine[0] == '#') {
172 // Comment/information line
173 std::string key1, key2;
174 std::istringstream inputLine(currentLine, std::ios::in);
175 inputLine.ignore(256, ' ');
176 inputLine >> key1 >> key2;
177
178 if (key2 == "Histograms") {
179 // NSpec (Format: 'nspec HISTOGRAM')
180 nSpec = std::stoi(key1);
181 g_log.information() << "Histogram Line: " << key1 << " nSpec = " << nSpec << "\n";
182 } else if (key1 == "Instrument:") {
183 // Instrument (Format: 'Instrument XXXX')
184 instrumentname = key2;
185 g_log.information() << "Instrument : " << key2 << "\n";
186 } else if (key1 == "with") {
187 // Multiply by bin width: (Format: 'with multiplied')
188 std::string s1;
189 inputLine >> s1;
190 if (s1 == "multiplied") {
191 multiplybybinwidth = true;
192 g_log.information() << "Y is multiplied by bin width\n";
193 } else {
194 g_log.warning() << "In line '" << currentLine << "', key word " << s1 << " is not allowed!\n";
195 }
196 } else if (key1 == "Primary") {
197 // Primary flight path ...
198 boost::smatch result;
199 // Have to force a copy of the input or the stack gets corrupted
200 // on MSVC when inputLine.str() falls out of scope which then
201 // corrupts the value in result
202 const std::string line = inputLine.str();
203 if (boost::regex_search(line, result, L1_REG_EXP) && result.size() == 2) {
204 primaryflightpath = std::stod(std::string(result[1]));
205
206 } else {
207 std::stringstream msg;
208 msg << "Failed to parse primary flight path from line \"" << inputLine.str() << "\"";
209 g_log.warning(msg.str());
210 }
211
212 std::stringstream msg;
213 msg << "L1 = " << primaryflightpath;
214 g_log.information(msg.str());
215 } else if (key1 == "Total") {
216 // Total flight path .... .... including total flying path, difc and
217 // 2theta of 1 bank
218
219 double totalpath(0.f);
220 double tth(0.f);
221 double difc(0.f);
222
223 boost::smatch result;
224 const std::string line = inputLine.str();
225 if (boost::regex_search(line, result, DET_POS_REG_EXP) && result.size() == 4) {
226 totalpath = std::stod(std::string(result[1]));
227 tth = std::stod(std::string(result[2]));
228 difc = std::stod(std::string(result[3]));
229 } else {
230 std::stringstream msg;
231 msg << "Failed to parse position from line \"" << inputLine.str() << "\"";
232 g_log.warning(msg.str());
233 }
234
235 totalflightpaths.emplace_back(totalpath);
236 twothetas.emplace_back(tth);
237 difcs.emplace_back(difc);
238
239 std::stringstream msg;
240 msg << "Bank " << difcs.size() - 1 << ": Total flight path = " << totalpath << " 2Theta = " << tth
241 << " DIFC = " << difc;
242 g_log.information(msg.str());
243 } // if keys....
244
245 } // ENDIF for Line with #
246 else if (currentLine[0] == 'B') {
247 // Line start with Bank including file format, X0 information and etc.
248 isOutOfHead = true;
249
250 // If there is, Save the previous to array and initialize new MantiVec for
251 // (X, Y, E)
252 if (!vecX.empty()) {
253 gsasDataX.emplace_back(std::move(vecX));
254 gsasDataY.emplace_back(std::move(vecY));
255 gsasDataE.emplace_back(std::move(vecE));
256 vecX.clear();
257 vecY.clear();
258 vecE.clear();
259
260 if (prog != nullptr)
261 prog->report();
262 }
263
264 // Parse the bank line in format
265 // RALF: BANK <SpectraNo> <NBins> <NBins> RALF <BC1> <BC2> <BC1> <BC4>
266 // SLOG: BANK <SpectraNo> <NBins> <NBins> SLOG <BC1> <BC2> <BC3> 0>
267 // where,
268 // BC1 = X[0] * 32
269 // BC2 = X[1] * 32 - BC1
270 // BC4 = ( X[1] - X[0] ) / X[0]
271
272 int specno, nbin1, nbin2;
273 std::istringstream inputLine(currentLine, std::ios::in);
274
275 double bc1 = 0;
276 double bc2 = 0;
277
278 inputLine.ignore(256, 'K');
279 std::string filetypestring;
280
281 inputLine >> specno >> nbin1 >> nbin2 >> filetypestring;
282 g_log.debug() << "Bank: " << specno << " filetypestring = " << filetypestring << '\n';
283
284 detectorIDs.emplace_back(specno);
285
286 if (filetypestring[0] == 'S') {
287 // SLOG
288 filetype = 's';
289 inputLine >> bc1 >> bc2 >> bc3 >> bc4;
290 } else if (filetypestring[0] == 'R') {
291 // RALF
292 filetype = 'r';
293 inputLine >> bc1 >> bc2 >> bc1 >> bc4;
294 } else {
295 g_log.error() << "Unsupported GSAS File Type: " << filetypestring << "\n";
296 throw Exception::FileError("Not a GSAS file", filename);
297 }
298
299 // Determine x0
300 if (filetype == 'r') {
301 double x0 = bc1 / 32;
302 g_log.debug() << "RALF: x0 = " << x0 << " bc4 = " << bc4 << '\n';
303 vecX.emplace_back(x0);
304 } else {
305 // Cannot calculate x0, turn on the flag
306 calslogx0 = true;
307 }
308 } // Line with B
309 else if (isOutOfHead) {
310 // Parse data line
311 double xValue;
312 double yValue;
313 double eValue;
314
315 double xPrev;
316
317 // * Get previous X value
318 if (!vecX.empty()) {
319 xPrev = vecX.back();
320 } else if (filetype == 'r') {
321 // Except if RALF
322 throw Mantid::Kernel::Exception::NotImplementedError("LoadGSS: File was not in expected format.");
323 } else {
324 xPrev = -0.0;
325 }
326
327 // It is different for the definition of X, Y, Z in SLOG and RALF format
328 if (filetype == 'r') {
329 // RALF
330 // LoadGSS produces overlapping columns for some datasets, due to
331 // std::setw
332 // For this reason we need to read the column values as string and then
333 // convert to double
334 {
335 std::string str(currentLine, 15);
336 std::istringstream istr(str);
337 istr >> xValue;
338 }
339 {
340 std::string str(currentLine + 15, 18);
341 std::istringstream istr(str);
342 istr >> yValue;
343 }
344 {
345 std::string str(currentLine + 15 + 18, 18);
346 std::istringstream istr(str);
347 istr >> eValue;
348 }
349
350 xValue = (2 * xValue) - xPrev;
351
352 } else if (filetype == 's') {
353 // SLOG
354 std::istringstream inputLine(currentLine, std::ios::in);
355 inputLine >> xValue >> yValue >> eValue;
356 if (calslogx0) {
357 // calculation of x0 must use the x'[0]
358 g_log.debug() << "x'_0 = " << xValue << " bc3 = " << bc3 << '\n';
359
360 double x0 = 2 * xValue / (bc3 + 2.0);
361 vecX.emplace_back(x0);
362 xPrev = x0;
363 g_log.debug() << "SLOG: x0 = " << x0 << '\n';
364 calslogx0 = false;
365 }
366
367 xValue = (2 * xValue) - xPrev;
368 } else {
369 g_log.error() << "Unsupported GSAS File Type: " << filetype << "\n";
370 throw Exception::FileError("Not a GSAS file", filename);
371 }
372
373 if (multiplybybinwidth) {
374 yValue = yValue / (xValue - xPrev);
375 eValue = eValue / (xValue - xPrev);
376 }
377
378 // store read in data (x, y, e) to vector
379 vecX.emplace_back(xValue);
380 vecY.emplace_back(yValue);
381 vecE.emplace_back(eValue);
382 } // Date Line
383 else {
384 g_log.warning() << "Line not defined: " << currentLine << '\n';
385 }
386 } // ENDWHILE of reading all lines
387
388 // Get the sizes before using std::move
389 auto nHist(static_cast<int>(gsasDataX.size()));
390 auto xWidth(static_cast<int>(vecX.size()));
391 auto yWidth(static_cast<int>(vecY.size()));
392
393 // Push the vectors (X, Y, E) of the last bank to gsasData
394 if (!vecX.empty()) { // Put final spectra into data
395 gsasDataX.emplace_back(std::move(vecX));
396 gsasDataY.emplace_back(std::move(vecY));
397 gsasDataE.emplace_back(std::move(vecE));
398 ++nHist;
399 }
400 input.close();
401
402 //********************************************************************************************
403 // Construct the workspace for GSS data
404 //********************************************************************************************
405
406 // Create workspace & GSS Files data is always in TOF
407
408 MatrixWorkspace_sptr outputWorkspace = std::dynamic_pointer_cast<MatrixWorkspace>(
409 WorkspaceFactory::Instance().create("Workspace2D", nHist, xWidth, yWidth));
410 outputWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
411
412 // set workspace title
413 if (filetype == 'r')
414 outputWorkspace->setTitle(wsTitle);
415 else
416 outputWorkspace->setTitle(slogTitle);
417
418 // put data from constructed histograms into outputWorkspace
419 if (detectorIDs.size() != static_cast<size_t>(nHist)) {
420 // File error is found
421 std::ostringstream mess("");
422 mess << "Number of spectra (" << detectorIDs.size() << ") is not equal to number of histograms (" << nHist << ").";
423 throw std::runtime_error(mess.str());
424 }
425
426 for (int i = 0; i < nHist; ++i) {
427 // Move data across
428 outputWorkspace->setHistogram(i, BinEdges(std::move(gsasDataX[i])), Counts(std::move(gsasDataY[i])),
429 CountStandardDeviations(std::move(gsasDataE[i])));
430
431 // Reset spectrum number if
432 if (useBankAsSpectrum) {
433 auto specno = static_cast<specnum_t>(detectorIDs[i]);
434 outputWorkspace->getSpectrum(i).setSpectrumNo(specno);
435 }
436 }
437
438 // build instrument geometry
439 createInstrumentGeometry(outputWorkspace, instrumentname, primaryflightpath, detectorIDs, totalflightpaths, twothetas,
440 difcs);
441
442 return outputWorkspace;
443}
444
445//----------------------------------------------------------------------------------------------
448double LoadGSS::convertToDouble(const std::string &inputstring) {
449 std::string temps;
450 auto isize = static_cast<int>(inputstring.size());
451 for (int i = 0; i < isize; i++) {
452 char thechar = inputstring[i];
453 if ((thechar <= 'Z' && thechar >= 'A') || (thechar <= 'z' && thechar >= 'a')) {
454 break;
455 } else {
456 temps += thechar;
457 }
458 }
459
460 double rd = std::stod(temps);
461
462 return rd;
463}
464
465//----------------------------------------------------------------------------------------------
468void LoadGSS::createInstrumentGeometry(const MatrixWorkspace_sptr &workspace, const std::string &instrumentname,
469 const double &primaryflightpath, const std::vector<int> &detectorids,
470 const std::vector<double> &totalflightpaths,
471 const std::vector<double> &twothetas, const std::vector<double> &difcs) {
472 // Check Input
473 if (detectorids.size() != totalflightpaths.size() || totalflightpaths.size() != twothetas.size()) {
474 g_log.warning("Cannot create geometry, because the numbers of L2 and Polar "
475 "are not equal.");
476 return;
477 }
478
479 // Debug output
480 std::stringstream dbss;
481 dbss << "L1 = " << primaryflightpath << "\n";
482 for (size_t i = 0; i < detectorids.size(); i++) {
483 dbss << "Detector " << detectorids[i] << " L1+L2 = " << totalflightpaths[i] << " 2Theta = " << twothetas[i]
484 << "\n";
485 }
486 g_log.debug(dbss.str());
487
488 // Create a new instrument and set its name
489 Geometry::Instrument_sptr instrument(new Geometry::Instrument(instrumentname));
490
491 // Add dummy source and samplepos to instrument
492 Geometry::Component *samplepos = new Geometry::Component("Sample", instrument.get());
493 instrument->add(samplepos);
494 instrument->markAsSamplePos(samplepos);
495 samplepos->setPos(0.0, 0.0, 0.0);
496
497 Geometry::ObjComponent *source = new Geometry::ObjComponent("Source", instrument.get());
498 instrument->add(source);
499 instrument->markAsSource(source);
500
501 double l1 = primaryflightpath;
502 source->setPos(0.0, 0.0, -1.0 * l1);
503
504 // Add detectors
505 // The L2 and 2-theta values from Raw file assumed to be relative to sample
506 // position
507 const auto numDetector = static_cast<int>(detectorids.size()); // number of detectors
508 // std::vector<int> detID = detectorids; // detector IDs
509 // std::vector<double> angle = twothetas; // angle between indicent beam and
510 // direction from sample to detector (two-theta)
511
512 // Assumption: detector IDs are in the same order of workspace index
513 for (int i = 0; i < numDetector; ++i) {
514 // a) Create a new detector. Instrument will take ownership of pointer so no
515 // need to delete.
516 Geometry::Detector *detector = new Geometry::Detector("det", detectorids[i], samplepos);
517 Kernel::V3D pos;
518
519 // r is L2
520 double r = totalflightpaths[i] - l1;
521 pos.spherical(r, twothetas[i], 0.0);
522
523 detector->setPos(pos);
524
525 // add copy to instrument, spectrum and mark it
526 auto &spec = workspace->getSpectrum(i);
527 spec.clearDetectorIDs();
528 spec.addDetectorID(detectorids[i]);
529 instrument->add(detector);
530 instrument->markAsDetector(detector);
531
532 } // ENDFOR (i: spectrum)
533 workspace->setInstrument(instrument);
534
535 auto &paramMap = workspace->instrumentParameters();
536 for (size_t i = 0; i < workspace->getNumberHistograms(); i++) {
537 auto detector = workspace->getDetector(i);
538 paramMap.addDouble(detector->getComponentID(), "DIFC", difcs[i]);
539 }
540}
541} // namespace Mantid::DataHandling
IPeaksWorkspace_sptr workspace
#define DECLARE_FILELOADER_ALGORITHM(classname)
DECLARE_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro when wri...
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
@ Load
allowed here which will be passed to the algorithm
A property class for workspaces.
Loads a file as saved by SaveGSS.
Definition LoadGSS.h:24
double convertToDouble(const std::string &inputstring)
Convert a string (value+unit) to double (value)
Definition LoadGSS.cpp:448
void createInstrumentGeometry(const API::MatrixWorkspace_sptr &workspace, const std::string &instrumentname, const double &primaryflightpath, const std::vector< int > &detectorids, const std::vector< double > &totalflightpaths, const std::vector< double > &twothetas, const std::vector< double > &difcs)
Create an instrument geometry.
Definition LoadGSS.cpp:468
void exec() override
Execution code.
Definition LoadGSS.cpp:93
int confidence(Kernel::FileDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
Definition LoadGSS.cpp:50
void init() override
Initialisation code.
Definition LoadGSS.cpp:77
API::MatrixWorkspace_sptr loadGSASFile(const std::string &filename, bool useBankAsSpectrum)
Main method to load GSAS.
Definition LoadGSS.cpp:107
Component is a wrapper for a Component which can modify some of its parameters, e....
Definition Component.h:42
void setPos(double, double, double) override
Set the IComponent position, x, y, z respective to parent (if present)
This class represents a detector - i.e.
Definition Detector.h:30
Base Instrument Class.
Definition Instrument.h:47
Object Component class, this class brings together the physical attributes of the component to the po...
Records the filename and the description of failure.
Definition Exception.h:98
Marks code as not implemented yet.
Definition Exception.h:138
Defines a wrapper around an open file.
static bool isAscii(const std::string &filename, const size_t nbytes=256)
Returns true if the file is considered ascii.
std::istream & data()
Access the open file stream.
const std::string & extension() const
Access the file extension.
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
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
std::shared_ptr< Instrument > Instrument_sptr
Shared pointer to an instrument object.
int32_t specnum_t
Typedef for a spectrum Number.
Definition IDTypes.h:14
@ Output
An output workspace.
Definition Property.h:54