Mantid
Loading...
Searching...
No Matches
LoadSpiceAscii.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#include <boost/algorithm/string.hpp>
8#include <fstream>
9
13#include "MantidAPI/Run.h"
14#include "MantidAPI/TableRow.h"
20
21#include <boost/algorithm/string/finder.hpp>
22#include <boost/algorithm/string/iter_find.hpp>
23
24using namespace boost::algorithm;
25
26using namespace Mantid::API;
27using namespace Mantid::Kernel;
28using namespace Mantid::DataHandling;
29using Mantid::Types::Core::DateAndTime;
30
31namespace Mantid::DataHandling {
32
33// DECLARE_FILELOADER_ALGORITHM(LoadSpiceAscii)
35
36static bool endswith(const std::string &s, const std::string &subs) {
37 // s is not long enough
38 if (s.size() < subs.size())
39 return false;
40
41 // get a substring
42 std::string tail = s.substr(s.size() - subs.size());
43
44 return tail == subs;
45}
46
47static bool checkIntersection(std::vector<std::string> v1, std::vector<std::string> v2) {
48 // Sort
49 std::sort(v1.begin(), v1.end());
50 std::sort(v2.begin(), v2.end());
51
52 // Check intersectiom
53 std::vector<std::string> intersectvec(v1.size() + v2.size());
54 auto outiter = std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), intersectvec.begin());
55 return static_cast<int>(outiter - intersectvec.begin()) != 0;
56}
57
58//----------------------------------------------------------------------------------------------
63const std::string LoadSpiceAscii::name() const { return "LoadSpiceAscii"; }
64
65//----------------------------------------------------------------------------------------------
70int LoadSpiceAscii::version() const { return 1; }
71
72//----------------------------------------------------------------------------------------------
75const std::string LoadSpiceAscii::category() const { return "DataHandling\\Text"; }
76
77//----------------------------------------------------------------------------------------------
80const std::string LoadSpiceAscii::summary() const { return "Load Spice data to workspaces in general."; }
81
82//----------------------------------------------------------------------------------------------
86 declareProperty(std::make_unique<FileProperty>("Filename", "", API::FileProperty::Load, ".dat"),
87 "Name of SPICE data file.");
88
89 // Logs to be float type sample log
90 auto floatspckeyprop = std::make_unique<ArrayProperty<std::string>>("FloatSampleLogNames", Direction::Input);
91 declareProperty(std::move(floatspckeyprop), "List of log names that will be imported as float property.");
92
93 // Logs to be integer type sample log
94 auto intspckeyprop = std::make_unique<ArrayProperty<std::string>>("IntegerSampleLogNames", Direction::Input);
95 declareProperty(std::move(intspckeyprop), "List of log names that will be imported as integer property.");
96
97 // Logs to be string type sample log
98 auto strspckeyprop = std::make_unique<ArrayProperty<std::string>>("StringSampleLogNames", Direction::Input);
99 declareProperty(std::move(strspckeyprop), "List of log names that will be imported as string property.");
100
101 declareProperty("IgnoreUnlistedLogs", false,
102 "If it is true, all log names are not listed in any of above "
103 "3 input lists will be ignored. "
104 "Otherwise, any log name is not listed will be treated as "
105 "string property.");
106
107 // date: MM/DD/YYYY,time: HH:MM:SS AM is the standard SPICE date time log name
108 // and format
109 std::vector<std::string> defaultlogformat(4);
110 defaultlogformat[0] = "date";
111 defaultlogformat[1] = "MM/DD/YYYY";
112 defaultlogformat[2] = "time";
113 defaultlogformat[3] = "HH:MM:SS AM";
114 declareProperty(std::make_unique<ArrayProperty<std::string>>("DateAndTimeLog", std::move(defaultlogformat)),
115 "Name and format for date and time");
116
117 // Output
118 declareProperty(std::make_unique<WorkspaceProperty<ITableWorkspace>>("OutputWorkspace", "", Direction::Output),
119 "Name of TableWorkspace containing experimental data.");
120
121 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("RunInfoWorkspace", "", Direction::Output),
122 "Name of TableWorkspace containing experimental information.");
123}
124
125//----------------------------------------------------------------------------------------------
129 // Input properties and validate
130 std::string filename = getPropertyValue("Filename");
131 std::vector<std::string> strlognames = getProperty("StringSampleLogNames");
132 std::vector<std::string> intlognames = getProperty("IntegerSampleLogNames");
133 std::vector<std::string> floatlognames = getProperty("FloatSampleLogNames");
134 bool ignoreunlisted = getProperty("IgnoreUnlistedLogs");
135 std::vector<std::string> datetimeprop = getProperty("DateAndTimeLog");
136
137 bool valid = validateLogNamesType(floatlognames, intlognames, strlognames);
138 if (!valid)
139 throw std::runtime_error("At one log name appears in multiple log type lists");
140
141 // Parse
142 std::vector<std::vector<std::string>> datalist;
143 std::vector<std::string> titles;
144 std::map<std::string, std::string> runinfodict;
145 parseSPICEAscii(filename, datalist, titles, runinfodict);
146
147 // Build output workspaces
148 API::ITableWorkspace_sptr outws = createDataWS(datalist, titles);
149
150 // Build run information workspace
151 API::MatrixWorkspace_sptr runinfows =
152 createRunInfoWS(runinfodict, floatlognames, intlognames, strlognames, ignoreunlisted);
153
154 // Process date and time for run start explicitly
155 setupRunStartTime(runinfows, datetimeprop);
156
157 // Set properties
158 setProperty("OutputWorkspace", outws);
159 setProperty("RunInfoWorkspace", runinfows);
160}
161
162//----------------------------------------------------------------------------------------------
170bool LoadSpiceAscii::validateLogNamesType(const std::vector<std::string> &floatlognames,
171 const std::vector<std::string> &intlognames,
172 const std::vector<std::string> &strlognames) {
173 std::vector<std::vector<std::string>> vec_lognamelist;
174 vec_lognamelist.emplace_back(floatlognames);
175 vec_lognamelist.emplace_back(intlognames);
176 vec_lognamelist.emplace_back(strlognames);
177
178 // Check whther there is any intersction among 3 sets
179 bool hascommon = false;
180 for (size_t i = 0; i < 3; ++i)
181 for (size_t j = i + 1; j < 3; ++j) {
182 hascommon = checkIntersection(vec_lognamelist[i], vec_lognamelist[j]);
183 if (hascommon) {
184 std::stringstream ess;
185 ess << "logsets[" << i << "] and log sets[" << j << "] has intersection.";
186 g_log.error(ess.str());
187 break;
188 }
189 }
190
191 return (!hascommon);
192}
193
194//----------------------------------------------------------------------------------------------
202void LoadSpiceAscii::parseSPICEAscii(const std::string &filename, std::vector<std::vector<std::string>> &datalist,
203 std::vector<std::string> &titles,
204 std::map<std::string, std::string> &runinfodict) {
205 // Import file
206 std::ifstream spicefile(filename.c_str());
207 if (!spicefile.is_open()) {
208 std::stringstream ess;
209 ess << "File " << filename << " cannot be opened.";
210 throw std::runtime_error(ess.str());
211 }
212
213 std::string line;
214 while (std::getline(spicefile, line)) {
215 // Parse one line
216
217 // Strip
218 boost::trim(line);
219 // skip for empyt line
220 if (line.empty())
221 continue;
222
223 // Comment line for run information
224 if (line[0] == '#') {
225 // remove comment flag # and trim space
226 line.erase(0, 1);
227 boost::trim(line);
228
229 if (line.find('=') != std::string::npos) {
230 // run information line
231 std::vector<std::string> terms;
232 boost::split(terms, line, boost::is_any_of("="));
233 boost::trim(terms[0]);
234 g_log.debug() << "Title = " << terms[0] << ", number of splitted terms = " << terms.size() << "\n";
235 std::string infovalue;
236 if (terms.size() == 2) {
237 infovalue = terms[1];
238 boost::trim(infovalue);
239 } else if (terms.size() > 2) {
240 // Content contains '='
241 for (size_t j = 1; j < terms.size(); ++j) {
242 if (j > 1)
243 infovalue += "=";
244 infovalue += terms[j];
245 }
246 } else {
247 std::stringstream wss;
248 wss << "Line '" << line << "' is hard to parse. It has more than 1 '='.";
249 g_log.warning(wss.str());
250 }
251 runinfodict.emplace(terms[0], infovalue);
252 } else if (line.find("Pt.") != std::string::npos) {
253 // Title line
254 boost::split(titles, line, boost::is_any_of("\t\n "), boost::token_compress_on);
255 } else if (endswith(line, "scan completed.")) {
256 std::vector<std::string> terms;
257 boost::iter_split(terms, line, boost::algorithm::first_finder("scan completed."));
258 std::string time = terms.front();
259 boost::trim(time);
260 runinfodict.emplace("runend", time);
261 } else {
262 // Not supported
263 std::stringstream wss;
264 wss << "File " << filename << ": line \"" << line << "\" cannot be parsed. It is ignored then.";
265 g_log.warning(wss.str());
266 }
267 } // If for run info
268 else {
269 // data line
270 std::vector<std::string> terms;
271 boost::split(terms, line, boost::is_any_of(" \t\n"), boost::token_compress_on);
272 datalist.emplace_back(terms);
273 }
274 }
275
276 g_log.debug() << "Run info dictionary has " << runinfodict.size() << " entries."
277 << "\n";
278}
279
280//----------------------------------------------------------------------------------------------
288API::ITableWorkspace_sptr LoadSpiceAscii::createDataWS(const std::vector<std::vector<std::string>> &datalist,
289 const std::vector<std::string> &titles) {
290 // Create a table workspace with columns defined
291 DataObjects::TableWorkspace_sptr outws = std::make_shared<DataObjects::TableWorkspace>();
292 size_t ipt = -1;
293 for (size_t i = 0; i < titles.size(); ++i) {
294 if (titles[i] == "Pt.") {
295 outws->addColumn("int", titles[i]);
296 ipt = i;
297 } else {
298 outws->addColumn("double", titles[i]);
299 }
300 }
301
302 // Add rows
303 size_t numrows = datalist.size();
304 size_t numcols = outws->columnCount();
305 for (size_t irow = 0; irow < numrows; ++irow) {
306 TableRow newrow = outws->appendRow();
307 for (size_t icol = 0; icol < numcols; ++icol) {
308 std::string item = datalist[irow][icol];
309 if (icol == ipt)
310 newrow << std::stoi(item);
311 else
312 newrow << std::stod(item);
313 }
314 }
315
316 ITableWorkspace_sptr tablews = std::dynamic_pointer_cast<ITableWorkspace>(outws);
317 return tablews;
318}
319
320//----------------------------------------------------------------------------------------------
330API::MatrixWorkspace_sptr LoadSpiceAscii::createRunInfoWS(std::map<std::string, std::string> runinfodict,
331 std::vector<std::string> &floatlognamelist,
332 std::vector<std::string> &intlognamelist,
333 std::vector<std::string> &strlognamelist,
334 bool ignoreunlisted) {
335 // Create an empty workspace
336 API::MatrixWorkspace_sptr infows = WorkspaceFactory::Instance().create("Workspace2D", 1, 2, 1);
337
338 // Sort
339 std::sort(floatlognamelist.begin(), floatlognamelist.end());
340 std::sort(intlognamelist.begin(), intlognamelist.end());
341 std::sort(strlognamelist.begin(), strlognamelist.end());
342
343 // Create sample log properties
344 std::map<std::string, std::string>::iterator miter;
345 for (miter = runinfodict.begin(); miter != runinfodict.end(); ++miter) {
346 const std::string title = miter->first;
347 const std::string strvalue = miter->second;
348
349 g_log.debug() << "Trying to add property " << title << " with value " << strvalue << "\n";
350
351 if (std::binary_search(floatlognamelist.begin(), floatlognamelist.end(), title)) {
352 // Case as a double property
353 bool adderrorvalue = false;
354 double value, error;
355
356 // Convert to float value and error (if exists)
357 if (strvalue.find("+/-") != std::string::npos) {
358 adderrorvalue = true;
359
360 std::vector<std::string> terms;
361 boost::iter_split(terms, strvalue, boost::algorithm::first_finder("+/-"));
362 value = std::stod(terms[0]);
363 error = std::stod(terms[1]);
364 } else {
365 value = std::stod(strvalue);
366 error = 0;
367 }
368
369 // Add properties
370 addProperty<double>(infows, title, value);
371 if (adderrorvalue) {
372 std::stringstream tss;
373 tss << title << ".error";
374 addProperty<double>(infows, tss.str(), error);
375 }
376 } else if (std::binary_search(intlognamelist.begin(), intlognamelist.end(), title)) {
377 // It is an integer log
378 addProperty<int>(infows, title, std::stoi(strvalue));
379 } else if (!ignoreunlisted || std::binary_search(strlognamelist.begin(), strlognamelist.end(), title)) {
380 // It is a string log or it is not defined but not ignored either
381 addProperty<std::string>(infows, title, strvalue);
382 }
383 }
384
385 return infows;
386}
387
388//----------------------------------------------------------------------------------------------
395 const std::vector<std::string> &datetimeprop) {
396 // Check if no need to process run start time
397 if (datetimeprop.empty()) {
398 g_log.information("User chooses not to set up run start date and time.");
399 return;
400 }
401
402 // Parse property vector
403 if (datetimeprop.size() != 4) {
404 g_log.warning() << "Run start date and time property must contain 4 "
405 "strings. User only specifies "
406 << datetimeprop.size() << ". Set up failed."
407 << "\n";
408 return;
409 }
410
411 // Parse
412 std::string datelogname = datetimeprop[0];
413 std::string timelogname = datetimeprop[2];
414 if (!(runinfows->run().hasProperty(datelogname) && runinfows->run().hasProperty(timelogname))) {
415 std::stringstream errss;
416 errss << "Unable to locate user specified date and time sample logs " << datelogname << " and " << timelogname
417 << "."
418 << "run_start will not be set up.";
419 g_log.error(errss.str());
420 return;
421 }
422
423 const std::string &rawdatestring = runinfows->run().getProperty(datelogname)->value();
424 const std::string &dateformat = datetimeprop[1];
425 std::string mtddatestring = processDateString(rawdatestring, dateformat);
426
427 const std::string &rawtimestring = runinfows->run().getProperty(timelogname)->value();
428 const std::string &timeformat = datetimeprop[3];
429 std::string mtdtimestring = processTimeString(rawtimestring, timeformat);
430
431 std::string mtddatetimestr = mtddatestring + "T" + mtdtimestring;
432
433 // Set up property
434 DateAndTime runstart(mtddatetimestr);
435 addProperty<std::string>(runinfows, "run_start", runstart.toISO8601String());
436}
437
438//----------------------------------------------------------------------------------------------
445std::string LoadSpiceAscii::processDateString(const std::string &rawdate, const std::string &dateformat) {
446 // Identify splitter
447 std::string splitter;
448 if (dateformat.find('/') != std::string::npos)
449 splitter += "/";
450 else if (dateformat.find('-') != std::string::npos)
451 splitter += "-";
452 else if (dateformat.find('.') != std::string::npos)
453 splitter += ".";
454 else
455 throw std::runtime_error("Input date format does not contain any of / - "
456 "or '.'. Format unsupported.");
457
458 // Split
459 std::vector<std::string> dateterms;
460 std::vector<std::string> formatterms;
461 boost::split(dateterms, rawdate, boost::is_any_of(splitter));
462 boost::split(formatterms, dateformat, boost::is_any_of(splitter));
463
464 if (dateterms.size() != formatterms.size() || dateterms.size() != 3)
465 throw std::runtime_error("Unsupported date string and format");
466 std::string year;
467 std::string month;
468 std::string day;
469 for (size_t i = 0; i < 3; ++i) {
470 if (formatterms[i].find('Y') != std::string::npos)
471 year = dateterms[i];
472 else if (formatterms[i].find('M') != std::string::npos) {
473 month = dateterms[i];
474 if (month.size() == 1)
475 month.insert(0, 1, '0');
476 } else {
477 day = dateterms[i];
478 if (day.size() == 1)
479 day.insert(0, 1, '0');
480 }
481 }
482
483 std::string formatdate = year + "-" + month + "-" + day;
484
485 return formatdate;
486}
487
488//----------------------------------------------------------------------------------------------
495std::string LoadSpiceAscii::processTimeString(const std::string &rawtime, const std::string &timeformat) {
496 // Process time format to find out it is 12 hour or 24 hour format
497 std::string timeformatcpy(timeformat);
498 boost::trim(timeformatcpy);
499
500 int format;
501 if (timeformatcpy.find(' ') == std::string::npos)
502 format = 24;
503 else
504 format = 12;
505
506 // Process
507 std::string mtdtime;
508 if (format == 24)
509 mtdtime = rawtime;
510 else {
511 std::vector<std::string> terms;
512 boost::split(terms, rawtime, boost::is_any_of(" "));
513 bool pm = false;
514 if (terms[1] == "PM")
515 pm = true;
516
517 std::vector<std::string> terms2;
518 boost::split(terms2, terms[0], boost::is_any_of(":"));
519 int hour = std::stoi(terms[0]);
520 if (hour < 12 && pm)
521 hour += 12;
522
523 std::stringstream hourss;
524 hourss << hour;
525 std::string hourstr = hourss.str();
526 if (hourstr.size() == 1)
527 hourstr = "0" + hourstr;
528 std::string minstr = terms2[1];
529 if (minstr.size() == 1)
530 minstr = "0" + minstr;
531 std::string secstr = terms2[2];
532 if (secstr.size() == 1)
533 secstr = "0" + secstr;
534
535 mtdtime = hourstr + ":" + minstr + ":" + secstr;
536 }
537
538 return mtdtime;
539}
540
541//----------------------------------------------------------------------------------------------
548template <typename T>
549void LoadSpiceAscii::addProperty(const API::MatrixWorkspace_sptr &ws, const std::string &pname, T pvalue) {
550 ws->mutableRun().addLogData(new PropertyWithValue<T>(pname, pvalue));
551}
552
553} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double value
The value of the point.
Definition: FitMW.cpp:51
double error
Definition: IndexPeaks.cpp:133
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
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
@ Load
allowed here which will be passed to the algorithm
Definition: FileProperty.h:52
TableRow represents a row in a TableWorkspace.
Definition: TableRow.h:39
A property class for workspaces.
LoadSpiceAscii : TODO: DESCRIPTION.
const std::string name() const override
Name.
void setupRunStartTime(const API::MatrixWorkspace_sptr &runinfows, const std::vector< std::string > &datetimeprop)
Set up run start time.
const std::string summary() const override
Summary.
bool validateLogNamesType(const std::vector< std::string > &floatlognames, const std::vector< std::string > &intlognames, const std::vector< std::string > &strlognames)
Check whether 3 sets of values have intersection.
int version() const override
Version.
void init() override
Declaration of properties.
const std::string category() const override
Category.
std::string processTimeString(const std::string &rawtime, const std::string &timeformat)
Convert input time string to mantid time string.
API::MatrixWorkspace_sptr createRunInfoWS(std::map< std::string, std::string > runinfodict, std::vector< std::string > &floatlognamelist, std::vector< std::string > &intlognamelist, std::vector< std::string > &strlognamelist, bool ignoreunlisted)
Create run information workspace.
std::string processDateString(const std::string &rawdate, const std::string &dateformat)
Convert input date string to mantid date string.
void parseSPICEAscii(const std::string &filename, std::vector< std::vector< std::string > > &datalist, std::vector< std::string > &titles, std::map< std::string, std::string > &runinfodict)
Parse SPICE Ascii file to dictionary.
API::ITableWorkspace_sptr createDataWS(const std::vector< std::vector< std::string > > &datalist, const std::vector< std::string > &titles)
Create data workspace.
void addProperty(const API::MatrixWorkspace_sptr &ws, const std::string &pname, T pvalue)
Add property to workspace.
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
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
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
static bool checkIntersection(std::vector< std::string > v1, std::vector< std::string > v2)
static bool endswith(const std::string &s, const std::string &subs)
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
STL namespace.
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54