19#include <boost/algorithm/string.hpp>
20#include <boost/regex.hpp>
35static const std::string IPARM_KEY(
"Instrument parameter file:");
36static const std::string L1_KEY(
"L1");
37static const std::string ZERO(
"0.");
38static const std::string EXP_INI_VAN_KEY(
"Vana");
39static const std::string EXP_INI_EMPTY_KEY(
"VanaBg");
40static const std::string EXP_INI_CAN_KEY(
"MTc");
43static const size_t INFO_OFFSET_V1(6);
45static const int F_INDEX_V0 = 0;
46static const int F_INDEX_V1 = 1;
47static const int F_INDEX_EXPINI = 2;
48static const int F_INDEX_SIZE = 3;
50const boost::regex V1_TABLE_REG_EXP{
"^freq.*\\s+w.*l.*\\s+"
52 "mt_env\\s+mt_instr(.+)"};
53const boost::regex VERSION_REG_EXP{
"^version=([0-9]+)"};
59std::vector<std::string> extra_columns(
const std::vector<std::string> &filenames) {
61 if (filenames[F_INDEX_V1].empty())
62 return std::vector<std::string>();
64 std::set<std::string> columnSet;
67 std::ifstream file(filenames[F_INDEX_V1].c_str(), std::ios_base::binary);
75 if (boost::regex_search(line, result, V1_TABLE_REG_EXP)) {
76 if (result.size() == 2) {
80 columnSet.insert(token);
89 std::vector<std::string> columnnames;
90 std::copy(columnSet.begin(), columnSet.end(), std::back_inserter(columnnames));
91 std::sort(columnnames.begin(), columnnames.end());
111 const auto exts = std::vector<std::string>({
".txt"});
112 declareProperty(std::make_unique<MultipleFileProperty>(
"Filename", exts),
"Characterizations file");
114 "(Optional) exp.ini file used at NOMAD");
117 "Output for the information of characterizations and runs");
123 "Spectrum Nos (note that it is not detector ID or workspace "
124 "indices). The list must be either empty or have a size "
125 "equal to input workspace's histogram number. ");
127 "Secondary flight (L2) paths for each detector. Number of L2 "
128 "given must be same as number of histogram.");
130 "Polar angles (two thetas) for detectors. Number of 2theta "
131 "given must be same as number of histogram.");
133 "Azimuthal angles (out-of-plane) for detectors. "
134 "Number of azimuthal angles given must be same as number of histogram.");
142 const std::vector<std::string> canColumnNames = extra_columns(filenames);
146 wksp->addColumn(
"double",
"frequency");
147 wksp->addColumn(
"double",
"wavelength");
148 wksp->addColumn(
"int",
"bank");
149 wksp->addColumn(
"str",
"vanadium");
150 wksp->addColumn(
"str",
"vanadium_background");
151 wksp->addColumn(
"str",
"container");
152 wksp->addColumn(
"str",
"empty_environment");
153 wksp->addColumn(
"str",
"empty_instrument");
154 wksp->addColumn(
"str",
"d_min");
155 wksp->addColumn(
"str",
"d_max");
156 wksp->addColumn(
"double",
"tof_min");
157 wksp->addColumn(
"double",
"tof_max");
158 wksp->addColumn(
"double",
"wavelength_min");
159 wksp->addColumn(
"double",
"wavelength_max");
160 for (
const auto &canColumnName : canColumnNames) {
161 wksp->addColumn(
"str", canColumnName);
171 this->
readExpIni(filenames[F_INDEX_EXPINI], wksp);
177int getVersion(
const std::string &filename) {
178 std::ifstream file(filename.c_str(), std::ios_base::binary);
179 if (!file.is_open()) {
186 boost::smatch result;
187 if (boost::regex_search(line, result, VERSION_REG_EXP) && result.size() == 2) {
188 return boost::lexical_cast<int>(result[1]);
196T lexical_cast(
const std::string &
value,
const std::string &filename,
const int linenum,
197 const std::string &label =
"") {
199 return boost::lexical_cast<T>(
value);
200 }
catch (boost::bad_lexical_cast &e) {
218 std::vector<std::string> filenamesFromPropertyUnraveld;
219 std::vector<std::vector<std::string>> filenamesFromProperty = this->
getProperty(
"Filename");
220 for (
const auto &outer : filenamesFromProperty) {
221 filenamesFromPropertyUnraveld.insert(filenamesFromPropertyUnraveld.end(), outer.cbegin(), outer.cend());
224 if (filenamesFromPropertyUnraveld.size() > 2) {
225 throw std::runtime_error(
"Can only specify up to 2 characterization files");
231 for (
size_t i = 0; i < filenamesFromPropertyUnraveld.size(); ++i) {
232 const int versionFromFile = getVersion(filenamesFromPropertyUnraveld[i]);
233 g_log.
debug() <<
"Found version " << versionFromFile <<
" in \"" << filenamesFromPropertyUnraveld[i] <<
"\"\n";
234 if (versionFromFile == 0)
235 v0_index =
static_cast<int>(i);
236 else if (versionFromFile == 1)
237 v1_index =
static_cast<int>(i);
241 std::vector<std::string> filenames(F_INDEX_SIZE);
243 filenames[F_INDEX_V0] = filenamesFromPropertyUnraveld[v0_index];
245 filenames[F_INDEX_V1] = filenamesFromPropertyUnraveld[v1_index];
248 std::string iniFilename = this->
getProperty(
"ExpIniFilename");
249 if (!iniFilename.empty()) {
250 filenames[F_INDEX_EXPINI] = iniFilename;
254 for (
const auto &filename : filenames) {
255 if (filename.empty())
258 std::filesystem::path file(filename);
259 if (!std::filesystem::exists(file))
261 std::filesystem::path path(filename);
262 if (!std::filesystem::is_regular_file(path))
283 if (peek ==
"#S" || peek ==
"#L")
286 std::vector<int32_t> specIds;
287 std::vector<double>
l2;
288 std::vector<double> polar;
289 std::vector<double> azi;
300 if (line.substr(0, 1) ==
"#")
303 std::vector<std::string> splitted;
304 boost::split(splitted, line, boost::is_any_of(
"\t "), boost::token_compress_on);
305 if (splitted[0] == L1_KEY) {
306 this->
setProperty(
"PrimaryFlightPath", lexical_cast<double>(splitted[1], filename, linenum,
"l1"));
308 }
else if (splitted.size() >= 3) {
309 specIds.emplace_back(lexical_cast<int32_t>(splitted[0], filename, linenum,
"spectrum number"));
310 l2.emplace_back(lexical_cast<double>(splitted[1], filename, linenum,
"l2"));
311 polar.emplace_back(lexical_cast<double>(splitted[2], filename, linenum,
"polar"));
312 if (splitted.size() >= 4 && (!splitted[3].empty())) {
313 azi.emplace_back(lexical_cast<double>(splitted[3], filename, linenum,
"azimuthal"));
315 azi.emplace_back(0.);
320 if (specIds.size() !=
l2.size() || specIds.size() != polar.size() || specIds.size() != azi.size())
348 g_log.
debug() <<
"readCharInfo(file, wksp)\n";
350 const size_t num_of_columns = wksp->columnCount();
359 if (line.substr(0, 1) ==
"#")
363 std::vector<std::string> splitted;
364 boost::split(splitted, line, boost::is_any_of(
"\t "), boost::token_compress_on);
365 while (splitted.size() < 12)
366 splitted.emplace_back(ZERO);
370 row << lexical_cast<double>(splitted[0], filename, linenum,
"frequency");
371 row << lexical_cast<double>(splitted[1], filename, linenum,
"wavelength");
372 row << lexical_cast<int32_t>(splitted[2], filename, linenum,
"bank");
380 row << lexical_cast<double>(splitted[8], filename, linenum,
"tof_min");
381 row << lexical_cast<double>(splitted[9], filename, linenum,
"tof_max");
382 row << lexical_cast<double>(splitted[10], filename, linenum,
"wavelength_min");
383 row << lexical_cast<double>(splitted[11], filename, linenum,
"wavelength_max");
386 for (
size_t i = 14; i < num_of_columns; ++i) {
394 if (filename.empty())
397 g_log.
debug() <<
"readVersion0(" << filename <<
", wksp)\n";
399 std::ifstream file(filename.c_str(), std::ios_base::binary);
400 if (!file.is_open()) {
407 if (firstLine.substr(0, IPARM_KEY.size()) == IPARM_KEY) {
427 const auto frequency = boost::lexical_cast<double>(values[0]);
428 const auto wavelength = boost::lexical_cast<double>(values[1]);
431 const size_t numRows = wksp->rowCount();
432 for (
size_t i = 0; i < numRows; ++i) {
433 const double frequencyRow = wksp->getRef<
double>(
"frequency", i);
434 const double wavelengthRow = wksp->getRef<
double>(
"wavelength", i);
435 if (closeEnough(frequency, frequencyRow) && closeEnough(wavelength, wavelengthRow)) {
436 return static_cast<int>(i);
444 const std::vector<std::string> &values) {
445 wksp->getRef<std::string>(
"vanadium", rowNum) = values[2];
446 wksp->getRef<std::string>(
"vanadium_background", rowNum) = values[3];
447 wksp->getRef<std::string>(
"empty_environment", rowNum) = values[4];
448 wksp->getRef<std::string>(
"empty_instrument", rowNum) = values[5];
449 for (
size_t i = 0; i < names.size(); ++i) {
450 const auto name = names[i];
451 wksp->getRef<std::string>(
name, rowNum) = values[i + INFO_OFFSET_V1];
458 if (filename.empty())
461 g_log.
debug() <<
"readVersion1(" << filename <<
", wksp)\n";
463 g_log.
information() <<
"Opening \"" << filename <<
"\" as a version 1 file\n";
464 std::ifstream file(filename.c_str(), std::ios_base::binary);
465 if (!file.is_open()) {
471 boost::smatch versionSearch;
472 if (boost::regex_search(line, versionSearch, VERSION_REG_EXP) && versionSearch.size() == 2) {
473 g_log.
debug() <<
"Found version " << versionSearch[1] <<
"\n";
481 std::vector<std::string> columnNames;
487 if (line.substr(0, 1) ==
"#")
491 boost::smatch v1TableSearch;
493 if (boost::regex_search(line, v1TableSearch, V1_TABLE_REG_EXP)) {
494 if (v1TableSearch.size() == 2) {
500 if (columnNames.empty())
505 std::vector<std::string> valuesAsStr;
507 if (valuesAsStr.size() < columnNames.size() + INFO_OFFSET_V1) {
508 std::stringstream msg;
509 msg <<
"Number of data columns (" << valuesAsStr.size() <<
") not compatible with number of column labels ("
510 << (columnNames.size() + INFO_OFFSET_V1) <<
")";
514 const int rowIndex = findRow(wksp, valuesAsStr);
517 updateRow(wksp,
static_cast<size_t>(rowIndex), columnNames, valuesAsStr);
521 row << lexical_cast<double>(valuesAsStr[0], filename, linenum,
"frequency");
522 row << lexical_cast<double>(valuesAsStr[1], filename, linenum,
"wavelength");
523 row << boost::lexical_cast<int32_t>(1);
524 row << valuesAsStr[2];
525 row << valuesAsStr[3];
527 row << valuesAsStr[4];
528 row << valuesAsStr[5];
536 for (
size_t i = INFO_OFFSET_V1; i < valuesAsStr.size(); ++i) {
537 row << valuesAsStr[i];
554 if (filename.empty())
557 g_log.
debug() <<
"readExpIni(" << filename <<
", wksp)\n";
559 const size_t rowCount = wksp->rowCount();
561 throw std::runtime_error(
"Characterizations file does not have any "
562 "characterizations information");
564 std::ifstream file(filename.c_str(), std::ios_base::binary);
565 if (!file.is_open()) {
575 if (line.substr(0, 1) ==
"#")
579 std::vector<std::string> splitted;
580 boost::split(splitted, line, boost::is_any_of(
"\t "), boost::token_compress_on);
581 if (splitted.size() < 2)
585 for (
size_t row = 0; row < rowCount; ++row) {
586 if (splitted[0] == EXP_INI_VAN_KEY) {
587 wksp->getRef<std::string>(
"vanadium", row) = splitted[1];
588 }
else if (splitted[0] == EXP_INI_EMPTY_KEY) {
589 wksp->getRef<std::string>(
"vanadium_background", row) = splitted[1];
590 }
else if (splitted[0] == EXP_INI_CAN_KEY) {
591 wksp->getRef<std::string>(
"container", row) = splitted[1];
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
TableRow represents a row in a TableWorkspace.
A property class for workspaces.
LoadPDCharacterizations : Load a characterization file used in Powder Diffraction Reduction.
void readExpIni(const std::string &filename, API::ITableWorkspace_sptr &wksp)
Parse the (optional) exp.ini file found on NOMAD.
const std::string name() const override
Algorithm's name for identification.
void readVersion1(const std::string &filename, API::ITableWorkspace_sptr &wksp)
void init() override
Initialize the algorithm's properties.
void readCharInfo(std::ifstream &file, API::ITableWorkspace_sptr &wksp, const std::string &filename, int linenum)
Parse the stream for the characterization file information.
void readVersion0(const std::string &filename, API::ITableWorkspace_sptr &wksp)
int readFocusInfo(std::ifstream &file, const std::string &filename)
Parse the stream for the focus positions and instrument parameter filename.
void exec() override
Execute the algorithm.
int version() const override
Algorithm's version for identification.
const std::string category() const override
Algorithm's category for identification.
std::vector< std::string > getFilenames()
This ignores the traditional interpretation of Mantid::API::MultipleFileProperty and flattens the arr...
Support for a property that holds an array of values.
Records the filename and the description of failure.
Records the filename, the description of failure and the line on which it happened.
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.
void information(const std::string &msg)
Logs at information level.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Iterator begin()
Iterator referring to first element in the container.
@ TOK_IGNORE_EMPTY
ignore empty tokens
Iterator end()
Iterator referring to the past-the-end element in the container.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
Mantid::Kernel::StringTokenizer tokenizer
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
MANTID_KERNEL_DLL std::string peekLine(std::istream &fh)
Peek at a line without extracting it from the stream.
MANTID_KERNEL_DLL std::string getLine(std::istream &fh)
Get a line and strip comments Use only for a single call.
MANTID_KERNEL_DLL bool withinRelativeDifference(T const x, T const y, S const tolerance)
Test whether x, y are within relative tolerance tol.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
@ Output
An output workspace.