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(const 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 for (const auto &miter : runinfodict) {
345 const std::string title = miter.first;
346 const std::string strvalue = miter.second;
347
348 g_log.debug() << "Trying to add property " << title << " with value " << strvalue << "\n";
349
350 if (std::binary_search(floatlognamelist.begin(), floatlognamelist.end(), title)) {
351 // Case as a double property
352 bool adderrorvalue = false;
353 double value, error;
354
355 // Convert to float value and error (if exists)
356 if (strvalue.find("+/-") != std::string::npos) {
357 adderrorvalue = true;
358
359 std::vector<std::string> terms;
360 boost::iter_split(terms, strvalue, boost::algorithm::first_finder("+/-"));
361 value = std::stod(terms[0]);
362 error = std::stod(terms[1]);
363 } else {
364 value = std::stod(strvalue);
365 error = 0;
366 }
367
368 // Add properties
369 addProperty<double>(infows, title, value);
370 if (adderrorvalue) {
371 std::stringstream tss;
372 tss << title << ".error";
373 addProperty<double>(infows, tss.str(), error);
374 }
375 } else if (std::binary_search(intlognamelist.begin(), intlognamelist.end(), title)) {
376 // It is an integer log
377 addProperty<int>(infows, title, std::stoi(strvalue));
378 } else if (!ignoreunlisted || std::binary_search(strlognamelist.begin(), strlognamelist.end(), title)) {
379 // It is a string log or it is not defined but not ignored either
380 addProperty<std::string>(infows, title, strvalue);
381 }
382 }
383
384 return infows;
385}
386
387//----------------------------------------------------------------------------------------------
394 const std::vector<std::string> &datetimeprop) {
395 // Check if no need to process run start time
396 if (datetimeprop.empty()) {
397 g_log.information("User chooses not to set up run start date and time.");
398 return;
399 }
400
401 // Parse property vector
402 if (datetimeprop.size() != 4) {
403 g_log.warning() << "Run start date and time property must contain 4 "
404 "strings. User only specifies "
405 << datetimeprop.size() << ". Set up failed."
406 << "\n";
407 return;
408 }
409
410 // Parse
411 std::string datelogname = datetimeprop[0];
412 std::string timelogname = datetimeprop[2];
413 if (!(runinfows->run().hasProperty(datelogname) && runinfows->run().hasProperty(timelogname))) {
414 std::stringstream errss;
415 errss << "Unable to locate user specified date and time sample logs " << datelogname << " and " << timelogname
416 << "."
417 << "run_start will not be set up.";
418 g_log.error(errss.str());
419 return;
420 }
421
422 const std::string &rawdatestring = runinfows->run().getProperty(datelogname)->value();
423 const std::string &dateformat = datetimeprop[1];
424 std::string mtddatestring = processDateString(rawdatestring, dateformat);
425
426 const std::string &rawtimestring = runinfows->run().getProperty(timelogname)->value();
427 const std::string &timeformat = datetimeprop[3];
428 std::string mtdtimestring = processTimeString(rawtimestring, timeformat);
429
430 std::string mtddatetimestr = mtddatestring + "T" + mtdtimestring;
431
432 // Set up property
433 DateAndTime runstart(mtddatetimestr);
434 addProperty<std::string>(runinfows, "run_start", runstart.toISO8601String());
435}
436
437//----------------------------------------------------------------------------------------------
444std::string LoadSpiceAscii::processDateString(const std::string &rawdate, const std::string &dateformat) {
445 // Identify splitter
446 std::string splitter;
447 if (dateformat.find('/') != std::string::npos)
448 splitter += "/";
449 else if (dateformat.find('-') != std::string::npos)
450 splitter += "-";
451 else if (dateformat.find('.') != std::string::npos)
452 splitter += ".";
453 else
454 throw std::runtime_error("Input date format does not contain any of / - "
455 "or '.'. Format unsupported.");
456
457 // Split
458 std::vector<std::string> dateterms;
459 std::vector<std::string> formatterms;
460 boost::split(dateterms, rawdate, boost::is_any_of(splitter));
461 boost::split(formatterms, dateformat, boost::is_any_of(splitter));
462
463 if (dateterms.size() != formatterms.size() || dateterms.size() != 3)
464 throw std::runtime_error("Unsupported date string and format");
465 std::string year;
466 std::string month;
467 std::string day;
468 for (size_t i = 0; i < 3; ++i) {
469 if (formatterms[i].find('Y') != std::string::npos)
470 year = dateterms[i];
471 else if (formatterms[i].find('M') != std::string::npos) {
472 month = dateterms[i];
473 if (month.size() == 1)
474 month.insert(0, 1, '0');
475 } else {
476 day = dateterms[i];
477 if (day.size() == 1)
478 day.insert(0, 1, '0');
479 }
480 }
481
482 std::string formatdate = year + "-" + month + "-" + day;
483
484 return formatdate;
485}
486
487//----------------------------------------------------------------------------------------------
494std::string LoadSpiceAscii::processTimeString(const std::string &rawtime, const std::string &timeformat) {
495 // Process time format to find out it is 12 hour or 24 hour format
496 std::string timeformatcpy(timeformat);
497 boost::trim(timeformatcpy);
498
499 int format;
500 if (timeformatcpy.find(' ') == std::string::npos)
501 format = 24;
502 else
503 format = 12;
504
505 // Process
506 std::string mtdtime;
507 if (format == 24)
508 mtdtime = rawtime;
509 else {
510 std::vector<std::string> terms;
511 boost::split(terms, rawtime, boost::is_any_of(" "));
512 bool pm = false;
513 if (terms[1] == "PM")
514 pm = true;
515
516 std::vector<std::string> terms2;
517 boost::split(terms2, terms[0], boost::is_any_of(":"));
518 int hour = std::stoi(terms[0]);
519 if (hour < 12 && pm)
520 hour += 12;
521
522 std::stringstream hourss;
523 hourss << hour;
524 std::string hourstr = hourss.str();
525 if (hourstr.size() == 1)
526 hourstr = "0" + hourstr;
527 std::string minstr = terms2[1];
528 if (minstr.size() == 1)
529 minstr = "0" + minstr;
530 std::string secstr = terms2[2];
531 if (secstr.size() == 1)
532 secstr = "0" + secstr;
533
534 mtdtime = hourstr + ":" + minstr + ":" + secstr;
535 }
536
537 return mtdtime;
538}
539
540//----------------------------------------------------------------------------------------------
547template <typename T>
548void LoadSpiceAscii::addProperty(const API::MatrixWorkspace_sptr &ws, const std::string &pname, T pvalue) {
549 ws->mutableRun().addLogData(new PropertyWithValue<T>(pname, pvalue));
550}
551
552} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
double error
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
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.
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.
API::MatrixWorkspace_sptr createRunInfoWS(const 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.
Support for a property that holds an array of values.
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
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