Mantid
Loading...
Searching...
No Matches
LoadPSIMuonBin.cpp
Go to the documentation of this file.
1
2// Mantid Repository : https://github.com/mantidproject/mantid
3//
4// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
5// NScD Oak Ridge National Laboratory, European Spallation Source,
6// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
7// SPDX - License - Identifier: GPL - 3.0 +
10#include "MantidAPI/Axis.h"
14#include "MantidAPI/TableRow.h"
20#include "MantidHistogramData/Histogram.h"
25#include "MantidKernel/Unit.h"
28
29#include <boost/algorithm/string.hpp>
30#include <boost/filesystem.hpp>
31
32#include <algorithm>
33#include <fstream>
34#include <map>
35#include <memory>
36
37namespace Mantid::DataHandling {
38
39namespace {
40const std::map<std::string, std::string> MONTHS{{"JAN", "01"}, {"FEB", "02"}, {"MAR", "03"}, {"APR", "04"},
41 {"MAY", "05"}, {"JUN", "06"}, {"JUL", "07"}, {"AUG", "08"},
42 {"SEP", "09"}, {"OCT", "10"}, {"NOV", "11"}, {"DEC", "12"}};
43const std::vector<std::string> PSI_MONTHS{"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
44 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
45constexpr int TEMPERATURE_FILE_MAX_SEARCH_DEPTH = 3;
46constexpr auto TEMPERATURE_FILE_EXT = ".mon";
47} // namespace
48
49DECLARE_FILELOADER_ALGORITHM(LoadPSIMuonBin)
50
51// Algorithm's name for identification. @see Algorithm::name
52const std::string LoadPSIMuonBin::name() const { return "LoadPSIMuonBin"; }
53
54// Algorithm's version for identification. @see Algorithm::version
55int LoadPSIMuonBin::version() const { return 1; }
56
57const std::string LoadPSIMuonBin::summary() const {
58 return "Loads a data file that is in PSI Muon Binary format into a "
59 "workspace (Workspace2D class).";
60}
61
62// Algorithm's category for identification. @see Algorithm::category
63const std::string LoadPSIMuonBin::category() const { return "DataHandling\\PSI"; }
64
66 auto &stream = descriptor.data();
67 Mantid::Kernel::BinaryStreamReader streamReader(stream);
68 std::string fileFormat;
69 streamReader.read(fileFormat, 2);
70 if (fileFormat != "1N") {
71 return 0;
72 }
73 return 90;
74}
75
76// version 1 however there is an issue open to create a version which
77// handles temperatures aswell
78bool LoadPSIMuonBin::loadMutipleAsOne() { return false; }
79
81 const std::vector<std::string> exts{".bin"};
82 declareProperty(std::make_unique<Mantid::API::FileProperty>("Filename", "", Mantid::API::FileProperty::Load, exts),
83 "The name of the Bin file to load");
84
85 const std::vector<std::string> extsTemps{".mon"};
86 declareProperty(std::make_unique<Mantid::API::FileProperty>("TemperatureFilename", "",
88 "The name of the temperature file to be loaded, this is optional as it "
89 "will be automatically searched for if not provided.");
90
91 declareProperty(std::make_unique<Mantid::API::WorkspaceProperty<Mantid::API::Workspace>>("OutputWorkspace", "",
93 "An output workspace.");
94
95 declareProperty("SearchForTempFile", true,
96 "If no temp file has been given decide whether the algorithm "
97 "will search for the temperature file.");
98
99 declareProperty("FirstGoodData", 0.0, "First good data in the OutputWorkspace's spectra", Kernel::Direction::Output);
100
101 declareProperty("LastGoodData", 0.0, "Last good data in the OutputWorkspace's spectra", Kernel::Direction::Output);
102
103 declareProperty("TimeZero", 0.0, "The TimeZero of the OutputWorkspace", Kernel::Direction::Output);
104
107 "This property should only be set in the GUI and is present to work with "
108 "the Muon GUI preprocessor.");
109
110 declareProperty("MainFieldDirection", 0, "The field direction of the magnetic field on the instrument",
112
114 "A vector of time zero values");
115
118 "TableWorkspace of time zeros for each spectra");
119
120 declareProperty("CorrectTime", true, "Boolean flag controlling whether time should be corrected by timezero.",
125 "Table or a group of tables with information about the "
126 "detector grouping stored in the file (if any).");
127 auto mustBePositive = std::make_shared<Kernel::BoundedValidator<specnum_t>>();
128 mustBePositive->setLower(0);
129 declareProperty("SpectrumMin", static_cast<specnum_t>(0), mustBePositive,
130 "Index number of the first spectrum to read\n");
131 declareProperty("SpectrumMax", static_cast<specnum_t>(EMPTY_INT()), mustBePositive,
132 "Index of last spectrum to read\n"
133 "(default the last spectrum)");
134}
135
137
138 if (sizeof(float) != 4) {
139 // assumptions made about the binary input won't work but since there is no
140 // way to guarantee floating points are 32 bits on all Operating systems
141 // here we are.
142 throw std::runtime_error("A float on this machine is not 32 bits");
143 }
144
145 std::string binFilename = getPropertyValue("Filename");
146
147 std::ifstream binFile(binFilename, std::ios::in | std::ios::binary);
148
149 Mantid::Kernel::BinaryStreamReader streamReader(binFile);
150 // Read the first two bytes into a string
151 std::string fileFormat;
152 streamReader.read(fileFormat, 2);
153 if (fileFormat != "1N") {
154 throw std::runtime_error("Loaded file is not of PSIMuonBin type (First 2 bytes != 1N)");
155 }
156
157 readInHeader(streamReader);
158 readInHistograms(streamReader);
159
160 binFile.close();
161
162 // Create the workspace stuff
164
165 auto sizeOfXForHistograms = m_histograms[0].size() + 1;
166 DataObjects::Workspace2D_sptr outputWorkspace = DataObjects::create<DataObjects::Workspace2D>(
168 Mantid::HistogramData::Histogram(Mantid::HistogramData::BinEdges(sizeOfXForHistograms)));
169
170 for (auto specNum = 0u; specNum < m_histograms.size(); ++specNum) {
171 outputWorkspace->mutableX(specNum) = m_xAxis;
172 outputWorkspace->mutableY(specNum) = m_histograms[specNum];
173 outputWorkspace->mutableE(specNum) = m_eAxis[specNum];
174 outputWorkspace->getSpectrum(specNum).setDetectorID(specNum + 1);
175 }
176
177 assignOutputWorkspaceParticulars(outputWorkspace);
178
179 // Set up for the Muon PreProcessor
180 // create empty dead time table
182
183 auto largestBinValue = outputWorkspace->x(0)[outputWorkspace->x(0).size() - 1];
184
185 // Since the arrray is all 0s before adding them this can't get min
186 // element so just get first element
187 auto lastGoodDataSpecIndex = static_cast<int>(m_header.lastGood[0]);
188 setProperty("LastGoodData", outputWorkspace->x(0)[lastGoodDataSpecIndex]);
189
190 double timeZero = 0.0;
191 std::vector<double> timeZeroList;
192 if (m_header.realT0[0] != 0) {
193 timeZero = *std::max_element(std::begin(m_header.realT0), std::end(m_header.realT0));
194 timeZeroList = std::vector<double>(std::begin(m_header.realT0), std::begin(m_header.realT0) + m_histograms.size());
195 } else {
196 timeZero = static_cast<double>(*std::max_element(std::begin(m_header.integerT0), std::end(m_header.integerT0)));
197 timeZeroList =
198 std::vector<double>(std::begin(m_header.integerT0), std::begin(m_header.integerT0) + m_histograms.size());
199 }
200
201 // If timeZero is bigger than the largest bin assume it refers to a bin's
202 // value
203 double absTimeZero = timeZero;
204 std::vector<double> correctedTimeZeroList;
205 if (timeZero > largestBinValue) {
206 absTimeZero = outputWorkspace->x(0)[static_cast<int>(std::floor(timeZero))];
207 std::transform(timeZeroList.cbegin(), timeZeroList.cend(), std::back_inserter(correctedTimeZeroList),
208 [&outputWorkspace](const auto timeZeroValue) {
209 return outputWorkspace->x(0)[static_cast<int>(std::floor(timeZeroValue))];
210 });
211 } else {
212 correctedTimeZeroList = timeZeroList;
213 }
214
215 setProperty("TimeZero", absTimeZero);
216 setProperty("TimeZeroList", correctedTimeZeroList);
217
218 // create time zero table
219 if (!getPropertyValue("TimeZeroTable").empty()) {
220 auto table = createTimeZeroTable(m_histograms.size(), correctedTimeZeroList);
221 setProperty("TimeZeroTable", table);
222 }
223
224 auto firstGoodDataSpecIndex = static_cast<int>(*std::max_element(m_header.firstGood, m_header.firstGood + 16));
225
226 setProperty("FirstGoodData", outputWorkspace->x(0)[firstGoodDataSpecIndex]);
227
228 // Time zero is when the pulse starts.
229 // The pulse should be at t=0 to be like ISIS data
230 // manually offset the data
231 auto correctTime = getProperty("CorrectTime");
232 if (correctTime) {
233 for (auto specNum = 0u; specNum < m_histograms.size(); ++specNum) {
234 auto &xData = outputWorkspace->mutableX(specNum);
235 std::transform(xData.cbegin(), xData.cend(), xData.begin(),
236 [absTimeZero](const auto &xValue) { return xValue - absTimeZero; });
237 }
238 }
239 setProperty("OutputWorkspace", extractSpectra(outputWorkspace));
240
241 // Set DetectorGroupingTable if needed
243}
244
245void LoadPSIMuonBin::setDetectorGroupingTable(const size_t &numSpec) {
246 if (getPropertyValue("DetectorGroupingTable").empty())
247 return;
249 std::dynamic_pointer_cast<Mantid::DataObjects::TableWorkspace>(
250 Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace"));
251 detectorTable->addColumn("vector_int", "detector");
252 for (size_t i = 0; i < numSpec; i++) {
253 std::vector<int> dets{static_cast<int>(i) + 1};
254 Mantid::API::TableRow row = detectorTable->appendRow();
255 row << dets;
256 }
257 setProperty("DetectorGroupingTable", detectorTable);
258}
259
260void LoadPSIMuonBin::makeDeadTimeTable(const size_t &numSpec) {
261 if (getPropertyValue("DeadTimeTable").empty())
262 return;
264 std::dynamic_pointer_cast<Mantid::DataObjects::TableWorkspace>(
265 Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace"));
266 assert(deadTimeTable);
267 deadTimeTable->addColumn("int", "spectrum");
268 deadTimeTable->addColumn("double", "dead-time");
269
270 for (size_t i = 0; i < numSpec; i++) {
271 Mantid::API::TableRow row = deadTimeTable->appendRow();
272 row << static_cast<int>(i) + 1 << 0.0;
273 }
274 setProperty("DeadTimeTable", deadTimeTable);
275}
276
277std::string LoadPSIMuonBin::getFormattedDateTime(const std::string &date, const std::string &time) {
278 std::string year;
279 if (date.size() == 11) {
280 year = date.substr(7, 4);
281 } else {
282 year = "20" + date.substr(7, 2);
283 }
284 return year + "-" + MONTHS.find(date.substr(3, 3))->second + "-" + date.substr(0, 2) + "T" + time;
285}
286
288 // The single variables in the header of the binary file:
289 // Should be at 3rd byte
290 streamReader >> m_header.tdcResolution;
291
292 // Should be at 5th byte
293 streamReader >> m_header.tdcOverflow;
294
295 // Should be at 7th byte
296 streamReader >> m_header.numberOfRuns;
297
298 // This may be 29 but set to 28
299 streamReader.moveStreamToPosition(28);
300 streamReader >> m_header.lengthOfHistograms;
301
302 // Should be at 31st byte
303 streamReader >> m_header.numberOfHistograms;
304
305 streamReader.moveStreamToPosition(424);
306 streamReader >> m_header.totalEvents;
307
308 streamReader.moveStreamToPosition(1012);
309 streamReader >> m_header.histogramBinWidth;
310
311 if (m_header.histogramBinWidth == 0) {
312 // If no histogram bin width found calculate it
314 static_cast<float>((625.E-6) / 8. * pow(static_cast<float>(2.), static_cast<float>(m_header.tdcResolution)));
315 }
316
317 streamReader.moveStreamToPosition(712);
318 streamReader >> m_header.monNumberOfevents;
319
320 streamReader.moveStreamToPosition(128); // numdef
321 streamReader >> m_header.numberOfDataRecordsFile;
322
323 // Should be at 130th byte
324 streamReader.moveStreamToPosition(130); // lendef
325 streamReader >> m_header.lengthOfDataRecordsBin;
326
327 // Should be at 132nd byte
328 streamReader.moveStreamToPosition(132); // kdafhi
330
331 // Should be at 134th Byte
332 streamReader.moveStreamToPosition(134); // khidaf
334
335 streamReader.moveStreamToPosition(654);
336 streamReader >> m_header.periodOfSave;
337
338 // Should be at 658th byte
339 streamReader >> m_header.periodOfMon;
340}
341
343 // The strings in the header of the binary file:
344 streamReader.moveStreamToPosition(138);
345 // Only pass 10 bytes into the string from stream
346 streamReader.read(m_header.sample, 10);
347
348 streamReader.moveStreamToPosition(148);
349 // Only pass 10 bytes into the string from stream
350 streamReader.read(m_header.temp, 10);
351
352 streamReader.moveStreamToPosition(158);
353 // Only pass 10 bytes into the string from stream
354 streamReader.read(m_header.field, 10);
355
356 streamReader.moveStreamToPosition(168);
357 // Only pass 10 bytes into the string from stream
358 streamReader.read(m_header.orientation, 10);
359
360 streamReader.moveStreamToPosition(860);
361 // Only pass 62 bytes into the string from stream
362 streamReader.read(m_header.comment, 62);
363
364 streamReader.moveStreamToPosition(218);
365 // Only pass 9 bytes into the string from stream
366 streamReader.read(m_header.dateStart, 9);
367
368 streamReader.moveStreamToPosition(227);
369 // Only pass 9 bytes into the string from stream
370 streamReader.read(m_header.dateEnd, 9);
371
372 streamReader.moveStreamToPosition(236);
373 // Only pass 8 bytes into the string from stream
374 streamReader.read(m_header.timeStart, 8);
375
376 streamReader.moveStreamToPosition(244);
377 // Only pass 8 bytes into the string from stream
378 streamReader.read(m_header.timeEnd, 8);
379
380 streamReader.moveStreamToPosition(60);
381 // Only pass 11 bytes into the string from stream
382 streamReader.read(m_header.monDeviation, 11);
383}
384
386 // The arrays in the header of the binary file:
387 for (auto i = 0u; i <= 5; ++i) {
388 streamReader.moveStreamToPosition(924 + (i * 4));
389 streamReader.read(m_header.labels_scalars[i], 4);
390
391 streamReader.moveStreamToPosition(670 + (i * 4));
392 streamReader >> m_header.scalars[i];
393 }
394
395 for (auto i = 6u; i < 18; ++i) {
396 streamReader.moveStreamToPosition(554 + ((i - 6) * 4));
397 streamReader.read(m_header.labels_scalars[i], 4);
398
399 streamReader.moveStreamToPosition(360 + ((i - 6) * 4));
400 streamReader >> m_header.scalars[i];
401 }
402
403 for (auto i = 0u; i <= 15; ++i) {
404 streamReader.moveStreamToPosition(948 + (i * 4));
405 streamReader.read(m_header.labelsOfHistograms[i], 4);
406
407 streamReader.moveStreamToPosition(458 + (i * 2));
408 streamReader >> m_header.integerT0[i];
409
410 streamReader.moveStreamToPosition(490 + (i * 2));
411 streamReader >> m_header.firstGood[i];
412
413 streamReader.moveStreamToPosition(522 + (i * 2));
414 streamReader >> m_header.lastGood[i];
415
416 streamReader.moveStreamToPosition(792 + (i * 4));
417 streamReader >> m_header.realT0[i];
418 }
419
420 for (auto i = 0u; i < 4; ++i) {
421 streamReader.moveStreamToPosition(716 + (i * 4));
422 streamReader >> m_header.temperatures[i];
423
424 streamReader.moveStreamToPosition(738 + (i * 4));
425 streamReader >> m_header.temperatureDeviation[i];
426
427 streamReader.moveStreamToPosition(72 + (i * 4));
428 streamReader >> m_header.monLow[i];
429
430 streamReader.moveStreamToPosition(88 + (i * 4));
431 streamReader >> m_header.monHigh[i];
432 }
433}
434
436 readSingleVariables(streamReader);
437 readStringVariables(streamReader);
438 readArrayVariables(streamReader);
439}
440
442 constexpr auto sizeInt32_t = sizeof(int32_t);
443 const auto headerSize = 1024;
445 for (auto histogramIndex = 0; histogramIndex < m_header.numberOfHistograms; ++histogramIndex) {
446 const auto offset = histogramIndex * m_header.numberOfDataRecordsHistogram * m_header.lengthOfDataRecordsBin;
447 std::vector<double> &nextHistogram = m_histograms[histogramIndex];
448 streamReader.moveStreamToPosition(offset * sizeInt32_t + headerSize);
449 nextHistogram.reserve(m_header.lengthOfHistograms);
450 for (auto rowIndex = 0; rowIndex < m_header.lengthOfHistograms; ++rowIndex) {
451 int32_t nextReadValue;
452 streamReader >> nextReadValue;
453 nextHistogram.emplace_back(nextReadValue);
454 }
455 }
456}
457
459 // Create a x axis, assumption that m_histograms will all be the same size,
460 // and that x will be 1 more in size than y
461 for (auto xIndex = 0u; xIndex <= m_histograms[0].size(); ++xIndex) {
462 m_xAxis.emplace_back(static_cast<double>(xIndex) * m_header.histogramBinWidth);
463 }
464
465 // Create Errors
466 for (const auto &histogram : m_histograms) {
467 std::vector<double> newEAxis;
468 for (auto eIndex = 0u; eIndex < m_histograms[0].size(); ++eIndex) {
469 newEAxis.emplace_back(sqrt(histogram[eIndex]));
470 }
471 m_eAxis.emplace_back(newEAxis);
472 }
473}
474
476 Mantid::API::Algorithm_sptr logAlg = createChildAlgorithm("AddSampleLog");
477 logAlg->setProperty("Workspace", ws);
478 return logAlg;
479}
480
483 alg->setProperty("InputWorkspace", ws);
484 alg->setProperty("OutputWorkspace", "not_used");
485 alg->setProperty("StartWorkspaceIndex", getPropertyValue("SpectrumMin"));
486 alg->setProperty("EndWorkspaceIndex", getPropertyValue("SpectrumMax"));
487 alg->executeAsChildAlg();
488 return alg->getProperty("OutputWorkspace");
489}
490
491void LoadPSIMuonBin::addToSampleLog(const std::string &logName, const std::string &logText,
493 auto alg = createSampleLogAlgorithm(ws);
494 alg->setProperty("LogType", "String");
495 alg->setProperty("LogName", logName);
496 alg->setProperty("LogText", logText);
497 alg->executeAsChildAlg();
498}
499
500void LoadPSIMuonBin::addToSampleLog(const std::string &logName, const double &logNumber,
502 auto alg = createSampleLogAlgorithm(ws);
503 alg->setProperty("LogType", "Number");
504 alg->setProperty("NumberType", "Double");
505 alg->setProperty("LogName", logName);
506 alg->setProperty("LogText", std::to_string(logNumber));
507 alg->executeAsChildAlg();
508}
509
510void LoadPSIMuonBin::addToSampleLog(const std::string &logName, const int &logNumber,
512 auto alg = createSampleLogAlgorithm(ws);
513 alg->setProperty("LogType", "Number");
514 alg->setProperty("NumberType", "Int");
515 alg->setProperty("LogName", logName);
516 alg->setProperty("LogText", std::to_string(logNumber));
517 alg->executeAsChildAlg();
518}
519
521 // Sort some workspace particulars
522 outputWorkspace->setTitle(m_header.sample + " - Run:" + std::to_string(m_header.numberOfRuns));
523
524 // Set Run Property goodfrm
525 outputWorkspace->mutableRun().addProperty("goodfrm", static_cast<int>(m_header.lengthOfHistograms));
526 outputWorkspace->mutableRun().addProperty("run_number", static_cast<int>(m_header.numberOfRuns));
527
528 // Set axis variables
529 outputWorkspace->setYUnit("Counts");
530 std::shared_ptr<Kernel::Units::Label> lblUnit =
531 std::dynamic_pointer_cast<Kernel::Units::Label>(Kernel::UnitFactory::Instance().create("Label"));
532 lblUnit->setLabel("Time", Kernel::Units::Symbol::Microsecond);
533 outputWorkspace->getAxis(0)->unit() = lblUnit;
534
535 // Set Start date and time and end date and time
538 try {
539 Mantid::Types::Core::DateAndTime start(startDate);
540 Mantid::Types::Core::DateAndTime end(endDate);
541 outputWorkspace->mutableRun().setStartAndEndTime(start, end);
542 } catch (const std::logic_error &) {
543 Mantid::Types::Core::DateAndTime start;
544 Mantid::Types::Core::DateAndTime end;
545 outputWorkspace->mutableRun().setStartAndEndTime(start, end);
546 g_log.warning("The date in the .bin file was invalid");
547 }
548
549 addToSampleLog("run_end", endDate, outputWorkspace);
550 addToSampleLog("run_start", startDate, outputWorkspace);
551
552 // Assume unit is at the end of the temperature
553 boost::trim_right(m_header.temp);
554 auto tempUnit = std::string(1, m_header.temp.at(m_header.temp.size() - 1));
555 addToSampleLog("sample_temp_unit", tempUnit, outputWorkspace);
556
557 // When poping the back off the temperature it is meant to remove the unit
558 m_header.temp.pop_back();
559 try {
560 double temperature = std::stod(m_header.temp);
561 addToSampleLog("sample_temp", temperature, outputWorkspace);
562 } catch (std::invalid_argument &) {
563 g_log.warning("The \"sample_temp\" could not be converted to a number for "
564 "the sample log so has been added as a string");
565 addToSampleLog("sample_temp", m_header.temp, outputWorkspace);
566 }
567
568 // Add The other temperatures as log
569 constexpr auto sizeOfTemps = sizeof(m_header.temperatures) / sizeof(*m_header.temperatures);
570 for (auto tempNum = 1u; tempNum < sizeOfTemps + 1; ++tempNum) {
571 if (m_header.temperatures[tempNum - 1] == 0)
572 // Break out of for loop
573 break;
574 addToSampleLog("Spectra " + std::to_string(tempNum) + " Temperature", m_header.temperatures[tempNum - 1],
575 outputWorkspace);
576 addToSampleLog("Spectra " + std::to_string(tempNum) + " Temperature Deviation",
577 m_header.temperatureDeviation[tempNum - 1], outputWorkspace);
578 }
579
580 outputWorkspace->setComment(m_header.comment);
581 addToSampleLog("Comment", m_header.comment, outputWorkspace);
582 addToSampleLog("Length of run", static_cast<double>(m_histograms[0].size()) * m_header.histogramBinWidth,
583 outputWorkspace);
584
585 boost::trim_right(m_header.field);
586 auto fieldUnit = std::string(1, m_header.field.at(m_header.field.size() - 1));
587 addToSampleLog("Field Unit", fieldUnit, outputWorkspace);
588 m_header.field.pop_back();
589 try {
590 auto field = std::stod(m_header.field);
591 addToSampleLog("sample_magn_field", field, outputWorkspace);
592 } catch (std::invalid_argument &) {
593 g_log.warning("The \"Field\" could not be converted to a number for "
594 "the sample log so has been added as a string");
595 addToSampleLog("sample_magn_field", m_header.field, outputWorkspace);
596 }
597
598 // get scalar labels and set spectra accordingly
599 constexpr auto sizeOfScalars = sizeof(m_header.scalars) / sizeof(*m_header.scalars);
600 for (auto i = 0u; i < sizeOfScalars; ++i) {
601 if (m_header.labels_scalars[i] == "NONE")
602 // Break out of for loop
603 break;
604 addToSampleLog("Scalar Label Spectra " + std::to_string(i), m_header.labels_scalars[i], outputWorkspace);
605 addToSampleLog("Scalar Spectra " + std::to_string(i), m_header.scalars[i], outputWorkspace);
606 }
607
608 constexpr auto sizeOfLabels = sizeof(m_header.labelsOfHistograms) / sizeof(*m_header.labelsOfHistograms);
609 for (auto i = 0u; i < sizeOfLabels; ++i) {
610 if (m_header.labelsOfHistograms[i] == "")
611 break;
612 std::string name = m_header.labelsOfHistograms[i];
613 // if empty name is present (i.e. just empty space)
614 // replace with default name:
615 // group_specNum
616 const bool isSpace = name.find_first_not_of(" ") == std::string::npos;
617 std::string label = isSpace ? "group_" + std::to_string(i + 1) : m_header.labelsOfHistograms[i];
618
619 addToSampleLog("Label Spectra " + std::to_string(i), label, outputWorkspace);
620 }
621
622 addToSampleLog("Orientation", m_header.orientation, outputWorkspace);
623
624 // first good and last good
625 constexpr auto sizeOfFirstGood = sizeof(m_header.firstGood) / sizeof(*m_header.firstGood);
626 for (size_t i = 0; i < sizeOfFirstGood; ++i) {
627 if (m_header.firstGood[i] == 0)
628 // Break out of for loop
629 break;
630 addToSampleLog("First good spectra " + std::to_string(i), m_header.firstGood[i], outputWorkspace);
631 addToSampleLog("Last good spectra " + std::to_string(i), m_header.lastGood[i], outputWorkspace);
632 }
633
634 addToSampleLog("TDC Resolution", m_header.tdcResolution, outputWorkspace);
635 addToSampleLog("TDC Overflow", m_header.tdcOverflow, outputWorkspace);
636 addToSampleLog("Spectra Length", m_header.lengthOfHistograms, outputWorkspace);
637 addToSampleLog("Number of Spectra", m_header.numberOfHistograms, outputWorkspace);
638 addToSampleLog("Mon number of events", m_header.monNumberOfevents, outputWorkspace);
639 addToSampleLog("Mon Period", m_header.periodOfMon, outputWorkspace);
640
641 if (m_header.monLow[0] == 0 && m_header.monHigh[0] == 0) {
642 addToSampleLog("Mon Low", 0.0, outputWorkspace);
643 addToSampleLog("Mon High", 0.0, outputWorkspace);
644 } else {
645 constexpr auto sizeOfMonLow = sizeof(m_header.monLow) / sizeof(*m_header.monLow);
646 for (auto i = 0u; i < sizeOfMonLow; ++i) {
647 if (m_header.monLow[i] == 0 || m_header.monHigh[i] == 0)
648 // Break out of for loop
649 break;
650 addToSampleLog("Mon Low " + std::to_string(i), m_header.monLow[i], outputWorkspace);
651 addToSampleLog("Mon High" + std::to_string(i), m_header.monHigh[i], outputWorkspace);
652 }
653 }
654
655 addToSampleLog("Mon Deviation", m_header.monDeviation, outputWorkspace);
656
657 if (m_header.realT0[0] != 0) {
658 // 16 is the max size of realT0
659 for (auto i = 0u; i < 16; ++i) {
660 if (m_header.realT0[i] == 0)
661 break;
662 addToSampleLog("realT0 " + std::to_string(i), m_header.realT0[i], outputWorkspace);
663 }
664 }
665
666 if (m_header.integerT0[0] != 0) {
667 // 16 is the max size of integerT0
668 for (auto i = 0u; i < 16; ++i) {
669 if (m_header.integerT0[i] == 0)
670 break;
671 addToSampleLog("integerT0 " + std::to_string(i), m_header.integerT0[i], outputWorkspace);
672 }
673 }
674
675 // Read in the temperature file if provided/found
676 try {
677 readInTemperatureFile(outputWorkspace);
678 } catch (std::invalid_argument &e) {
679 g_log.warning("Temperature file was not be loaded: " + std::string(e.what()));
680 } catch (std::runtime_error &e) {
681 g_log.warning("Temperature file was not be loaded:" + std::string(e.what()));
682 }
683}
684
685namespace {
686std::string findTitlesFromLine(const std::string &line) {
687 bool titlesFound = false;
688 std::string foundTitles = "";
689 for (const auto &charecter : line) {
690 if (charecter == ':') {
691 titlesFound = true;
692 } else if (titlesFound) {
693 foundTitles += charecter;
694 }
695 }
696 return foundTitles;
697}
698} // namespace
699
700void LoadPSIMuonBin::processTitleHeaderLine(const std::string &line) {
701 auto foundTitles = findTitlesFromLine(line);
702 boost::trim(foundTitles);
703 if (foundTitles.find("\\") == std::string::npos) {
704 boost::split(m_tempHeader.titles, foundTitles, boost::is_any_of(" "));
706 } else {
707 boost::split(m_tempHeader.titles, foundTitles, boost::is_any_of("\\"));
709 }
710}
711
712void LoadPSIMuonBin::processDateHeaderLine(const std::string &line) {
713 // Assume the date is added in the same place as always
714 // line example = "! 2018-01-01 10:10:10"
715 // date = 2018-01-01
716 // _time = 10:10:10
717 auto date = line.substr(2, 11);
718 auto _time = line.substr(14, 8);
720}
721
722void LoadPSIMuonBin::processHeaderLine(const std::string &line) {
723 if (line.find("Title") != std::string::npos) {
724 // Find sample log titles from the header
726 } else if (std::find(std::begin(PSI_MONTHS), std::end(PSI_MONTHS), line.substr(5, 3)) != std::end(PSI_MONTHS)) {
727 // If the line contains a Month in the PSI format then assume it conains a
728 // date on the line. 5 is the index of the line that is where the month is
729 // found and 3 is the length of the month.
731 }
732}
733
734void LoadPSIMuonBin::readInTemperatureFileHeader(const std::string &contents) {
735 const int uselessLines = 6;
736 int lineNo = 0;
737 std::string line = "";
738 for (const auto charecter : contents) {
739 if (charecter == '\n') {
740 if (line.empty() || line[0] != '!') {
741 return;
742 } else if (lineNo > uselessLines) {
743 processHeaderLine(line);
744 }
745 ++lineNo;
746 line = "";
747 } else {
748 line += charecter;
749 }
750 }
751}
752
754 std::vector<std::string> segments;
755 boost::split(segments, line, boost::is_any_of("\\"));
756
757 // 5 is the size that we expect vectors to be at this stage
758 if (segments.size() != 5) {
759 throw std::runtime_error("Line does not have 5 segments delimited by \\: '" + line + "'");
760 }
761 const auto recordTime = segments[0];
762 const auto numValues = std::stoi(segments[1]);
763 std::vector<std::string> firstValues;
764 boost::split(firstValues, segments[2], boost::is_any_of(" "));
765 std::vector<std::string> secondValues;
766 boost::split(secondValues, segments[3], boost::is_any_of(" "));
767
768 // example recordTime = 10:10:10
769 // 10 hours, 10 minutes, and 10 seconds. Hence the substr choices here.
770 double secondsInRecordTime = (std::stoi(recordTime.substr(0, 2)) * 60 * 60) + // Hours
771 (std::stoi(recordTime.substr(3, 2)) * 60) + // Minutes
772 (std::stoi(recordTime.substr(6, 2))); // Seconds
773 const auto timeLog = (Types::Core::DateAndTime(m_tempHeader.startDateTime) + secondsInRecordTime).toISO8601String();
774
775 Mantid::API::Algorithm_sptr logAlg = createChildAlgorithm("AddTimeSeriesLog");
776 logAlg->setProperty("Workspace", ws);
777 logAlg->setProperty("Time", timeLog);
779 for (auto i = 0; i < numValues; ++i) {
780 logAlg->setProperty("Name", "Temp_" + m_tempHeader.titles[i]);
781 logAlg->setProperty("Type", "double");
782 logAlg->setProperty("Value", firstValues[i]);
783 logAlg->executeAsChildAlg();
784 }
785 } else {
786 logAlg->setProperty("Name", "Temp_" + m_tempHeader.titles[0]);
787 logAlg->setProperty("Type", "double");
788 logAlg->setProperty("Value", firstValues[0]);
789 logAlg->executeAsChildAlg();
790
791 logAlg->setProperty("Name", "Temp_" + m_tempHeader.titles[1]);
792 logAlg->setProperty("Type", "double");
793 logAlg->setProperty("Value", secondValues[0]);
794 logAlg->executeAsChildAlg();
795 }
796}
797
799 // Perform a breadth-first search starting from
800 // directory containing the main file. The search has
801 // a fixed limited depth to ensure we don't accidentally
802 // crawl the while filesystem.
803 namespace fs = boost::filesystem;
804 const fs::path searchDir{fs::path{getPropertyValue("Filename")}.parent_path()};
805
806 std::deque<fs::path> queue{fs::path{searchDir}};
807 while (!queue.empty()) {
808 const auto first = queue.front();
809 queue.pop_front();
810 for (fs::directory_iterator dirIter{first}; dirIter != fs::directory_iterator(); ++dirIter) {
811 const auto &entry{dirIter->path()};
812
813 if (fs::is_directory(entry)) {
814 const auto relPath{entry.lexically_relative(searchDir)};
815 if (std::distance(relPath.begin(), relPath.end()) < TEMPERATURE_FILE_MAX_SEARCH_DEPTH) {
816 // save the directory for searching when we have exhausted
817 // the file entries at this level
818 queue.push_back(entry);
819 }
820 } else if (entry.extension() == TEMPERATURE_FILE_EXT &&
821 entry.filename().string().find(std::to_string(m_header.numberOfRuns)) != std::string::npos) {
822 return entry.string();
823 }
824 }
825 }
826
827 return "";
828}
829
831 std::string fileName = getPropertyValue("TemperatureFilename");
832 const bool searchForTempFile = getProperty("SearchForTempFile");
833 if (fileName == "" && searchForTempFile) {
834 fileName = detectTempFile();
835 }
836
837 if (fileName == "") {
838 throw std::invalid_argument("No temperature file could be found/was provided");
839 }
840
841 g_log.notice("Temperature file in use by LoadPSIMuonBin: '" + fileName + "'");
842
843 std::ifstream in(fileName, std::ios::in);
844 std::string contents;
845 in.seekg(0, std::ios::end);
846 contents.resize(in.tellg());
847 in.seekg(0, std::ios::beg);
848 in.read(&contents[0], contents.size());
849 in.close();
850
852
853 std::string line = "";
854 for (const auto &character : contents) {
855 if (character == '\n') {
856 if (!line.empty() && line[0] == '!') {
857 line = "";
858 } else {
859 processLine(line, ws);
860 line = "";
861 }
862 } else {
863 line += character;
864 }
865 }
866}
867
868} // namespace Mantid::DataHandling
#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.
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
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Definition: Algorithm.cpp:842
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Definition: FileProperty.h:53
@ 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.
std::vector< std::vector< double > > m_eAxis
bool loadMutipleAsOne() override
Returns a value indicating whether or not loader wants to load multiple files into a single workspace...
void readInHeader(Mantid::Kernel::BinaryStreamReader &streamReader)
int version() const override
function to return a version of the algorithm, must be overridden in all algorithms
void exec() override
Virtual method - must be overridden by concrete algorithm.
void readArrayVariables(Mantid::Kernel::BinaryStreamReader &streamReader)
void readInTemperatureFileHeader(const std::string &contents)
void processDateHeaderLine(const std::string &line)
struct temperatureHeaderData m_tempHeader
const std::string summary() const override
function returns a summary message that will be displayed in the default GUI, and in the help.
void readInHistograms(Mantid::Kernel::BinaryStreamReader &streamReader)
void readInTemperatureFile(DataObjects::Workspace2D_sptr &ws)
void init() override
Virtual method - must be overridden by concrete algorithm.
std::vector< std::vector< double > > m_histograms
void processLine(const std::string &line, DataObjects::Workspace2D_sptr &ws)
void readSingleVariables(Mantid::Kernel::BinaryStreamReader &streamReader)
void processTitleHeaderLine(const std::string &line)
Mantid::API::Algorithm_sptr createSampleLogAlgorithm(DataObjects::Workspace2D_sptr &ws)
void processHeaderLine(const std::string &line)
void assignOutputWorkspaceParticulars(DataObjects::Workspace2D_sptr &outputWorkspace)
int confidence(Kernel::FileDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
const std::string category() const override
function to return a category of the algorithm.
void makeDeadTimeTable(const size_t &numSpec)
API::MatrixWorkspace_sptr extractSpectra(DataObjects::Workspace2D_sptr &ws)
void addToSampleLog(const std::string &logName, const std::string &logText, DataObjects::Workspace2D_sptr &ws)
const std::string name() const override
function to return a name of the algorithm, must be overridden in all algorithms
void setDetectorGroupingTable(const size_t &numSpec)
std::string getFormattedDateTime(const std::string &date, const std::string &time)
void readStringVariables(Mantid::Kernel::BinaryStreamReader &streamReader)
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
Assists with reading a binary file by providing standard overloads for the istream operators (>>) to ...
BinaryStreamReader & read(std::vector< int16_t > &value, const size_t nvals)
Read an array of int16_t into the given vector.
void moveStreamToPosition(size_t nbytes)
Move the stream to nbytes past the beginning of the file.
Defines a wrapper around an open file.
std::istream & data()
Access the open file stream.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void notice(const std::string &msg)
Logs at notice level.
Definition: Logger.cpp:95
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
static const UnitLabel Microsecond
Microsecond.
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition: Algorithm.h:61
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
DataObjects::TableWorkspace_sptr createTimeZeroTable(const size_t numSpec, const std::vector< double > &timeZeros)
Creates a timezero table for the loaded detectors.
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
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.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition: EmptyValues.h:25
int32_t specnum_t
Typedef for a spectrum Number.
Definition: IDTypes.h:16
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54