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