Mantid
Loading...
Searching...
No Matches
LoadMuonNexus1.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 +
8
9#include "MantidAPI/Axis.h"
12#include "MantidAPI/Progress.h"
15#include "MantidAPI/TableRow.h"
28#include "MantidKernel/Unit.h"
33// clang-format off
34#include <nexus/NeXusFile.hpp>
35#include <nexus/NeXusException.hpp>
36// clang-format on
37
38#include <boost/iterator/counting_iterator.hpp>
39#include <boost/scoped_array.hpp>
40
41#include <Poco/Path.h>
42
43#include <algorithm>
44#include <cmath>
45#include <memory>
46#include <limits>
47
48namespace Mantid::DataHandling {
49using namespace DataObjects;
50
51// Register the algorithm into the algorithm factory
53
54using namespace Kernel;
55using namespace API;
56using namespace Mantid::NeXus;
57using HistogramData::BinEdges;
58using HistogramData::Counts;
59
62
71 // Retrieve the filename from the properties
72 m_filename = getPropertyValue("Filename");
73 // Retrieve the entry number
74 m_entrynumber = getProperty("EntryNumber");
75
76 NXRoot root(m_filename);
77 NXEntry entry = root.openEntry("run/histogram_data_1");
78 try {
79 NXInfo info = entry.getDataSetInfo("time_zero");
80 if (info.stat != NX_ERROR) {
81 double dum = root.getFloat("run/histogram_data_1/time_zero");
82 setProperty("TimeZero", dum);
83 }
84 } catch (...) {
85 }
86
87 try {
88 NXInfo infoResolution = entry.getDataSetInfo("resolution");
89 NXInt counts = root.openNXInt("run/histogram_data_1/counts");
90 std::string firstGoodBin = counts.attributes("first_good_bin");
91 if (!firstGoodBin.empty() && infoResolution.stat != NX_ERROR) {
92 double resolution;
93
94 switch (infoResolution.type) {
95 case NX_FLOAT32:
96 resolution = static_cast<double>(entry.getFloat("resolution"));
97 break;
98 case NX_INT32:
99 resolution = static_cast<double>(entry.getInt("resolution"));
100 break;
101 default:
102 throw std::runtime_error("Unsupported data type for resolution");
103 }
104
105 auto bin = static_cast<double>(boost::lexical_cast<int>(firstGoodBin));
106 double bin_size = resolution / 1000000.0;
107
108 setProperty("FirstGoodData", bin * bin_size);
109 }
110 } catch (std::exception &e) {
111 g_log.warning() << "Error while loading the FirstGoodData value: " << e.what() << "\n";
112 }
113
114 try {
115 NXInfo infoResolution = entry.getDataSetInfo("resolution");
116 NXInt counts = root.openNXInt("run/histogram_data_1/counts");
117 std::string lastGoodBin = counts.attributes("last_good_bin");
118 if (!lastGoodBin.empty() && infoResolution.stat != NX_ERROR) {
119 double resolution;
120
121 switch (infoResolution.type) {
122 case NX_FLOAT32:
123 resolution = static_cast<double>(entry.getFloat("resolution"));
124 break;
125 case NX_INT32:
126 resolution = static_cast<double>(entry.getInt("resolution"));
127 break;
128 default:
129 throw std::runtime_error("Unsupported data type for resolution");
130 }
131
132 auto bin = static_cast<double>(boost::lexical_cast<int>(lastGoodBin));
133 double bin_size = resolution / 1000000.0;
134
135 setProperty("LastGoodData", bin * bin_size);
136 }
137 } catch (std::exception &e) {
138 g_log.warning() << "Error while loading the LastGoodData value: " << e.what() << "\n";
139 }
140
141 NXEntry nxRun = root.openEntry("run");
142 std::string title;
143 std::string notes;
144 try {
145 title = nxRun.getString("title");
146 notes = nxRun.getString("notes");
147 } catch (...) {
148 }
149 std::string run_num;
150 try {
151 run_num = std::to_string(nxRun.getInt("number"));
152 } catch (...) {
153 }
154
155 MuonNexusReader nxload;
156 nxload.readFromFile(m_filename);
157
158 // Read in the instrument name from the Nexus file
160 // Read in the number of spectra in the Nexus file
161 m_numberOfSpectra = nxload.t_nsp1;
162 if (m_entrynumber != 0) {
164 if (m_entrynumber > nxload.t_nper) {
165 throw std::invalid_argument("Invalid Entry Number:Enter a valid number");
166 }
167 } else {
168 // Read the number of periods in this file
169 m_numberOfPeriods = nxload.t_nper;
170 }
171
172 bool autoGroup = getProperty("AutoGroup");
173
174 // Grouping info should be returned if user has set the property
175 bool returnGrouping = !getPropertyValue("DetectorGroupingTable").empty();
176
177 // Call private method to validate the optional parameters, if set
179 // Calculate the size of a workspace, given its number of periods & spectra to
180 // read
181 int64_t total_specs;
182 if (m_interval || m_list) {
183 // Remove from list possible duplicate specs
184 for (auto it = m_spec_list.begin(); it != m_spec_list.end();) {
185 if ((*it >= m_spec_min) && (*it <= m_spec_max)) {
186 it = m_spec_list.erase(it);
187 } else {
188 ++it;
189 }
190 }
191 total_specs = m_spec_list.size();
192 if (m_interval) {
193 total_specs += (m_spec_max - m_spec_min + 1);
194 m_spec_max += 1;
195 }
196 } else {
197 total_specs = m_numberOfSpectra;
198 // for nexus return all spectra
199 m_spec_min = 1;
200 m_spec_max = m_numberOfSpectra + 1; // Add +1 to iterate
201 }
202
203 // Read the number of time channels (i.e. bins) from the Nexus file
204 const int channelsPerSpectrum = nxload.t_ntc1;
205 // Read in the time bin boundaries
206 const int lengthIn = channelsPerSpectrum + 1;
207
208 // Try to load dead time info
209 loadDeadTimes(root);
210
211 // Create the 2D workspace for the output
212 DataObjects::Workspace2D_sptr localWorkspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
213 WorkspaceFactory::Instance().create("Workspace2D", total_specs, lengthIn, lengthIn - 1));
214 localWorkspace->setTitle(title);
215 localWorkspace->setComment(notes);
216 localWorkspace->mutableRun().addLogData(new PropertyWithValue<std::string>("run_number", run_num));
217
218 // Add 'FirstGoodData' to list of logs if possible
219 if (existsProperty("FirstGoodData") && existsProperty("TimeZero")) {
220 double fgd = getProperty("FirstGoodData");
221 double tz = getProperty("TimeZero");
222 localWorkspace->mutableRun().addLogData(new PropertyWithValue<double>("FirstGoodData", fgd - tz));
223 }
224 // Set the unit on the workspace to muon time, for now in the form of a Label
225 // Unit
226 std::shared_ptr<Kernel::Units::Label> lblUnit =
227 std::dynamic_pointer_cast<Kernel::Units::Label>(UnitFactory::Instance().create("Label"));
228 lblUnit->setLabel("Time", Units::Symbol::Microsecond);
229 localWorkspace->getAxis(0)->unit() = lblUnit;
230 // Set y axis unit
231 localWorkspace->setYUnit("Counts");
232
234
235 API::Progress progress(this, 0.0, 1.0, m_numberOfPeriods * total_specs);
236 // Loop over the number of periods in the Nexus file, putting each period in a
237 // separate workspace
238 for (int64_t period = 0; period < m_numberOfPeriods; ++period) {
239 if (m_entrynumber != 0) {
240 period = m_entrynumber - 1;
241 if (period != 0) {
242 loadRunDetails(localWorkspace);
243 runLoadInstrument(localWorkspace);
244 }
245 }
246
247 if (period == 0) {
248 // Only run the Child Algorithms once
249 loadRunDetails(localWorkspace);
250 runLoadInstrument(localWorkspace);
251 runLoadLog(localWorkspace);
252 localWorkspace->populateInstrumentParameters();
253 } else // We are working on a higher period of a multiperiod raw file
254 {
255 localWorkspace =
256 std::dynamic_pointer_cast<DataObjects::Workspace2D>(WorkspaceFactory::Instance().create(localWorkspace));
257 localWorkspace->setTitle(title);
258 localWorkspace->setComment(notes);
259 }
260 addPeriodLog(localWorkspace, period);
261 addGoodFrames(localWorkspace, period, nxload.t_nper);
262 addToSampleLog("period_sequences", nxload.m_numPeriodSequences, localWorkspace);
263 addToSampleLog("period_labels", nxload.m_periodNames, localWorkspace);
264 addToSampleLog("period_type", nxload.m_periodTypes, localWorkspace);
265 addToSampleLog("frames_period_requested", nxload.m_framesPeriodsRequested, localWorkspace);
266 addToSampleLog("frames_period_raw", nxload.m_framesPeriodsRaw, localWorkspace);
267 addToSampleLog("period_output", nxload.m_periodsOutput, localWorkspace);
268 addToSampleLog("total_counts_period", nxload.m_periodsCounts, localWorkspace);
269
270 size_t counter = 0;
271 for (auto i = m_spec_min; i < m_spec_max; ++i) {
272 // Shift the histogram to read if we're not in the first period
273 auto histToRead = static_cast<specnum_t>(i - 1 + period * nxload.t_nsp1);
274 auto specNo = i;
275 loadData(counter, histToRead, specNo, nxload, lengthIn - 1,
276 localWorkspace); // added -1 for NeXus
277 counter++;
278 progress.report();
279 }
280 // Read in the spectra in the optional list parameter, if set
281 if (m_list) {
282 for (auto specid : m_spec_list) {
283 auto histToRead = static_cast<specnum_t>(specid - 1 + period * nxload.t_nsp1);
284 auto specNo = static_cast<specnum_t>(specid);
285 loadData(counter, histToRead, specNo, nxload, lengthIn - 1, localWorkspace);
286 counter++;
287 progress.report();
288 }
289 }
290 // Just a sanity check
291 assert(counter == size_t(total_specs));
292
293 Workspace_sptr outWs;
294
295 Workspace_sptr loadedGrouping;
296
297 // Try to load detector grouping info, if needed for auto-grouping or user
298 // requested it
299 if (autoGroup || returnGrouping) {
300 loadedGrouping = loadDetectorGrouping(root, localWorkspace->getInstrument());
301
302 if (loadedGrouping && returnGrouping) {
303 // Return loaded grouping, if requested
304 setProperty("DetectorGroupingTable", loadedGrouping);
305 }
306
307 if (!loadedGrouping && autoGroup) {
308 // If autoGroup requested and no grouping in the file - show a warning
309 g_log.warning("Unable to load grouping from the file. Grouping not applied.");
310 }
311 }
312
313 if (autoGroup && loadedGrouping) {
314 TableWorkspace_sptr groupingTable;
315
316 if (auto table = std::dynamic_pointer_cast<TableWorkspace>(loadedGrouping)) {
317 groupingTable = table;
318 } else if (auto group = std::dynamic_pointer_cast<WorkspaceGroup>(loadedGrouping)) {
319 groupingTable = std::dynamic_pointer_cast<TableWorkspace>(group->getItem(period));
320 }
321 std::vector<int> specIDs, detecIDs;
322 for (size_t i = 0; i < localWorkspace->getNumberHistograms(); i++) {
323 specIDs.emplace_back(localWorkspace->getSpectrum(i).getSpectrumNo());
324 detecIDs.emplace_back(localWorkspace->getSpectrum(i).getSpectrumNo());
325 }
326 API::SpectrumDetectorMapping mapping(specIDs, detecIDs);
327 localWorkspace->updateSpectraUsing(mapping);
328
329 Algorithm_sptr groupDet = createChildAlgorithm("MuonGroupDetectors");
330 groupDet->setProperty("InputWorkspace", localWorkspace);
331 groupDet->setProperty("DetectorGroupingTable", groupingTable);
332 groupDet->execute();
333
334 MatrixWorkspace_sptr groupedWs = groupDet->getProperty("OutputWorkspace");
335
336 outWs = groupedWs;
337 } else {
338 outWs = localWorkspace;
339 }
340
341 if (existsProperty("TimeZero")) {
342 auto timeZeroList = std::vector<double>(m_numberOfSpectra, getProperty("TimeZero"));
343 setProperty("TimeZeroList", timeZeroList);
344 if (!getPropertyValue("TimeZeroTable").empty())
345 setProperty("TimeZeroTable", createTimeZeroTable(m_numberOfSpectra, timeZeroList));
346 }
347
348 if (m_numberOfPeriods == 1)
349 setProperty("OutputWorkspace", outWs);
350 else
351 // In case of multiple periods, just add workspace to the group, and we
352 // will return the
353 // group later
354 wsGrpSptr->addWorkspace(outWs);
355
356 } // loop over periods
357
358 if (m_numberOfPeriods > 1) {
359 setProperty("OutputWorkspace", std::dynamic_pointer_cast<Workspace>(wsGrpSptr));
360 }
361}
362
368 // If dead times workspace name is empty - caller doesn't need dead times
369 if (getPropertyValue("DeadTimeTable").empty())
370 return;
371
372 NXEntry detector = root.openEntry("run/instrument/detector");
373
374 NXInfo infoDeadTimes = detector.getDataSetInfo("deadtimes");
375 if (infoDeadTimes.stat != NX_ERROR) {
376 NXFloat deadTimesData = detector.openNXFloat("deadtimes");
377 deadTimesData.load();
378
379 int numDeadTimes = deadTimesData.dim0();
380
381 std::vector<int> specToLoad;
382 std::vector<double> deadTimes;
383
384 // Set the spectrum list that should be loaded
385 if (m_interval || m_list) {
386 // Load only selected spectra
387 specToLoad.insert(specToLoad.end(), boost::counting_iterator<specnum_t>(m_spec_min),
388 boost::counting_iterator<specnum_t>(m_spec_max));
389 specToLoad.insert(specToLoad.end(), m_spec_list.begin(), m_spec_list.end());
390 } else {
391 // Load all the spectra
392 // Start from 1 to N+1 to be consistent with
393 // the case where spectra are specified
394 for (int i = 1; i < numDeadTimes / m_numberOfPeriods + 1; i++)
395 specToLoad.emplace_back(i);
396 }
397
398 if (numDeadTimes < m_numberOfSpectra) {
399 // Check number of dead time entries match the number of
400 // spectra in the nexus file
401 throw Exception::FileError("Number of dead times specified is less than number of spectra", m_filename);
402
403 } else if (numDeadTimes % m_numberOfSpectra) {
404
405 // At least, number of dead times should cover the number of spectra
406 throw Exception::FileError("Number of dead times doesn't cover every spectrum in every period", m_filename);
407 } else {
408
409 if (m_numberOfPeriods == 1) {
410 // Simplest case - one dead time for one detector
411
412 // Populate deadTimes
413 deadTimes.reserve(specToLoad.size());
414 std::transform(specToLoad.cbegin(), specToLoad.cend(), std::back_inserter(deadTimes),
415 [&deadTimesData](const auto &spectra) { return deadTimesData[spectra - 1]; });
416 // Load into table
417 TableWorkspace_sptr table = createDeadTimeTable(specToLoad, deadTimes);
418 setProperty("DeadTimeTable", table);
419
420 } else if (numDeadTimes == m_numberOfSpectra) {
421 // Multiple periods, but the same dead times for each
422
423 specToLoad.clear();
424 for (int i = 1; i < numDeadTimes + 1; i++) {
425 specToLoad.emplace_back(i);
426 }
427 deadTimes.reserve(specToLoad.size());
428 std::transform(specToLoad.cbegin(), specToLoad.cend(), std::back_inserter(deadTimes),
429 [deadTimesData](const auto &spectra) { return deadTimesData[spectra - 1]; });
430 // Load into table
431 TableWorkspace_sptr table = createDeadTimeTable(specToLoad, deadTimes);
432 setProperty("DeadTimeTable", table);
433 } else {
434 // More complex case - different dead times for different periods
435 WorkspaceGroup_sptr tableGroup = std::make_shared<WorkspaceGroup>();
436
437 for (int64_t i = 0; i < m_numberOfPeriods; i++) {
438
439 // Populate deadTimes
440 deadTimes.reserve(specToLoad.size());
441 for (auto spec : specToLoad) {
442 auto index = static_cast<int>(spec - 1 + i * m_numberOfSpectra);
443 deadTimes.emplace_back(deadTimesData[index]);
444 }
445
446 // Load into table
447 TableWorkspace_sptr table = createDeadTimeTable(specToLoad, deadTimes);
448
449 tableGroup->addWorkspace(table);
450 }
451
452 setProperty("DeadTimeTable", tableGroup);
453 }
454 }
455 }
456 // It is expected that file might not contain any dead times, so not finding
457 // them is not an
458 // error
459}
460
469 NXEntry dataEntry = root.openEntry("run/histogram_data_1");
470
471 NXInfo infoGrouping = dataEntry.getDataSetInfo("grouping");
472 if (infoGrouping.stat != NX_ERROR) {
473 NXInt groupingData = dataEntry.openNXInt("grouping");
474 groupingData.load();
475
476 int numGroupingEntries = groupingData.dim0();
477
478 std::vector<int> specToLoad;
479 std::vector<int> grouping;
480
481 // Set the spectrum list that should be loaded
482 if (m_interval || m_list) {
483 // Load only selected spectra
484 specToLoad.insert(specToLoad.end(), boost::counting_iterator<specnum_t>(m_spec_min),
485 boost::counting_iterator<specnum_t>(m_spec_max));
486 specToLoad.insert(specToLoad.end(), m_spec_list.begin(), m_spec_list.end());
487 } else {
488 // Load all the spectra
489 // Start from 1 to N+1 to be consistent with
490 // the case where spectra are specified
491 for (int i = 1; i < m_numberOfSpectra + 1; i++)
492 specToLoad.emplace_back(i);
493 }
494
495 if (numGroupingEntries < m_numberOfSpectra) {
496 // Check number of dead time entries match the number of
497 // spectra in the nexus file
498 throw Exception::FileError("Number of grouping entries is less than number of spectra", m_filename);
499
500 } else if (numGroupingEntries % m_numberOfSpectra) {
501 // At least the number of entries should cover all the spectra
502 throw Exception::FileError("Number of grouping entries doesn't cover "
503 "every spectrum in every period",
504 m_filename);
505
506 } else {
507
508 if (m_numberOfPeriods == 1) {
509 // Simplest case - one grouping entry per spectrum
510 grouping.reserve(grouping.size() + specToLoad.size());
511 if (!m_entrynumber) {
512 // m_entrynumber = 0 && m_numberOfPeriods = 1 means that user did not
513 // select
514 // any periods but there is only one in the dataset
515 std::transform(specToLoad.cbegin(), specToLoad.cend(), std::back_inserter(grouping),
516 [&groupingData](const auto spec) { return groupingData[spec - 1]; });
517 } else {
518 // User selected an entry number
519 for (auto &spec : specToLoad) {
520 int index = spec - 1 + static_cast<int>((m_entrynumber - 1) * m_numberOfSpectra);
521 grouping.emplace_back(groupingData[index]);
522 }
523 }
524
525 TableWorkspace_sptr table = createDetectorGroupingTable(specToLoad, grouping);
526
527 if (table->rowCount() != 0)
528 return table;
529
530 } else if (numGroupingEntries == m_numberOfSpectra) {
531 // Multiple periods - same grouping for each
532 specToLoad.clear();
533 for (int i = 1; i < m_numberOfSpectra + 1; i++) {
534 specToLoad.emplace_back(i);
535 }
536 std::transform(specToLoad.cbegin(), specToLoad.cend(), std::back_inserter(grouping),
537 [&groupingData](const auto spectrum) { return groupingData[spectrum - 1]; });
538 // Load into table
539 TableWorkspace_sptr table = createDetectorGroupingTable(specToLoad, grouping);
540 if (table->rowCount() != 0)
541 return table;
542 } else {
543 // More complex case - grouping information for every period
544
545 WorkspaceGroup_sptr tableGroup = std::make_shared<WorkspaceGroup>();
546
547 for (int i = 0; i < m_numberOfPeriods; i++) {
548
549 // Get the grouping
550 grouping.clear();
551 for (auto &spec : specToLoad) {
552 int index = spec - 1 + i * static_cast<int>(m_numberOfSpectra);
553 grouping.emplace_back(groupingData[index]);
554 }
555
556 // Set table for period i
557 TableWorkspace_sptr table = createDetectorGroupingTable(specToLoad, grouping);
558
559 // Add table to group
560 if (table->rowCount() != 0)
561 tableGroup->addWorkspace(table);
562 }
563
564 if (tableGroup->size() != 0) {
565 if (tableGroup->size() != static_cast<size_t>(m_numberOfPeriods))
566 throw Exception::FileError("Zero grouping for some of the periods", m_filename);
567
568 return tableGroup;
569 }
570 }
571 }
572 }
573 // If we reach this point, no/zero grouping found.
574 // Try to load from IDF instead
575 const std::string mainFieldDirection = getProperty("MainFieldDirection");
576 API::GroupingLoader groupLoader(inst, mainFieldDirection);
577 try {
578 const auto idfGrouping = groupLoader.getGroupingFromIDF();
579 g_log.warning("Loading grouping from IDF");
580 return idfGrouping->toTable();
581 } catch (const std::runtime_error &) {
582 g_log.warning("Loading dummy grouping");
583 auto dummyGrouping = std::make_shared<Grouping>();
584 if (inst->getNumberDetectors() != 0) {
585 dummyGrouping = groupLoader.getDummyGrouping();
586 } else {
587 // Make sure it uses the right number of detectors
588 std::ostringstream oss;
589 oss << "1-" << m_numberOfSpectra;
590 dummyGrouping->groups.emplace_back(oss.str());
591 dummyGrouping->description = "Dummy grouping";
592 dummyGrouping->groupNames.emplace_back("all");
593 }
594 return dummyGrouping->toTable();
595 }
596}
597
604TableWorkspace_sptr LoadMuonNexus1::createDeadTimeTable(std::vector<int> specToLoad, std::vector<double> deadTimes) {
605 TableWorkspace_sptr deadTimeTable =
606 std::dynamic_pointer_cast<TableWorkspace>(WorkspaceFactory::Instance().createTable("TableWorkspace"));
607
608 deadTimeTable->addColumn("int", "spectrum");
609 deadTimeTable->addColumn("double", "dead-time");
610
611 for (size_t i = 0; i < specToLoad.size(); i++) {
612 TableRow row = deadTimeTable->appendRow();
613 row << specToLoad[i] << deadTimes[i];
614 }
615
616 return deadTimeTable;
617}
618
627 std::vector<int> grouping) {
628 auto detectorGroupingTable =
629 std::dynamic_pointer_cast<TableWorkspace>(WorkspaceFactory::Instance().createTable("TableWorkspace"));
630
631 detectorGroupingTable->addColumn("vector_int", "Detectors");
632
633 std::map<int, std::vector<int>> groupingMap;
634
635 for (size_t i = 0; i < specToLoad.size(); i++) {
636 // Add detector ID to the list of group detectors. Detector ID is always
637 // spectra index + 1
638 groupingMap[grouping[i]].emplace_back(specToLoad[i]);
639 }
640
641 for (auto &group : groupingMap) {
642 if (group.first != 0) // Skip 0 group
643 {
644 TableRow newRow = detectorGroupingTable->appendRow();
645 newRow << group.second;
646 }
647 }
648
649 return detectorGroupingTable;
650}
651
661void LoadMuonNexus1::loadData(size_t hist, specnum_t &i, specnum_t specNo, MuonNexusReader &nxload,
662 const int64_t lengthIn, const DataObjects::Workspace2D_sptr &localWorkspace) {
663 // Read in a spectrum
664 // Put it into a vector, discarding the 1st entry, which is rubbish
665 // But note that the last (overflow) bin is kept
666 // For Nexus, not sure if above is the case, hence give all data for now
667
668 // Create and fill another vector for the X axis
669 std::vector<float> timeChannels(lengthIn + 1);
670 nxload.getTimeChannels(timeChannels.data(), static_cast<int>(lengthIn + 1));
671 // Put the read in array into a vector (inside a shared pointer)
672 localWorkspace->setHistogram(
673 hist, BinEdges(timeChannels.data(), timeChannels.data() + lengthIn + 1),
674 Counts(nxload.m_counts.begin() + i * lengthIn, nxload.m_counts.begin() + i * lengthIn + lengthIn));
675
676 localWorkspace->getSpectrum(hist).setSpectrumNo(specNo);
677 // Muon v1 files: always a one-to-one mapping between spectra and detectors
678 localWorkspace->getSpectrum(hist).setDetectorID(static_cast<detid_t>(specNo));
679}
680
685 API::Run &runDetails = localWorkspace->mutableRun();
686
687 runDetails.addProperty("run_title", localWorkspace->getTitle(), true);
688
689 auto numSpectra = static_cast<int>(localWorkspace->getNumberHistograms());
690 runDetails.addProperty("nspectra", numSpectra);
691
692 NXRoot root(m_filename);
693 try {
694 std::string start_time = root.getString("run/start_time");
695 runDetails.addProperty("run_start", start_time);
696 } catch (std::runtime_error &) {
697 g_log.warning("run/start_time is not available, run_start log not added.");
698 }
699
700 try {
701 std::string stop_time = root.getString("run/stop_time");
702 runDetails.addProperty("run_end", stop_time);
703 } catch (std::runtime_error &) {
704 g_log.warning("run/stop_time is not available, run_end log not added.");
705 }
706
707 try {
708 std::string dur = root.getString("run/duration");
709 runDetails.addProperty("dur", dur);
710 runDetails.addProperty("durunits", 1); // 1 means second here
711 runDetails.addProperty("dur_secs", dur);
712 } catch (std::runtime_error &) {
713 g_log.warning("run/duration is not available, dur log not added.");
714 }
715
716 // Get sample parameters
717 NXEntry runSample = root.openEntry("run/sample");
718
719 if (runSample.containsDataSet("temperature")) {
720 float temperature = runSample.getFloat("temperature");
721 runDetails.addProperty("sample_temp", static_cast<double>(temperature));
722 }
723
724 if (runSample.containsDataSet("magnetic_field")) {
725 float magn_field = runSample.getFloat("magnetic_field");
726 runDetails.addProperty("sample_magn_field", static_cast<double>(magn_field));
727 }
728}
729
732 auto loadLog = createChildAlgorithm("LoadMuonLog");
733 // Pass through the same input filename
734 loadLog->setPropertyValue("Filename", m_filename);
735 // Set the workspace property to be the same one filled above
736 loadLog->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
737
738 // Now execute the Child Algorithm. Catch and log any error, but don't stop.
739 try {
740 loadLog->execute();
741 } catch (std::runtime_error &) {
742 g_log.error("Unable to successfully run LoadMuonLog Child Algorithm");
743 } catch (std::logic_error &) {
744 g_log.error("Unable to successfully run LoadMuonLog Child Algorithm");
745 }
746
747 if (!loadLog->isExecuted())
748 g_log.error("Unable to successfully run LoadMuonLog Child Algorithm");
749
750 NXRoot root(m_filename);
751
752 // Get main field direction
753 std::string mainFieldDirection = "Longitudinal"; // default
754 try {
755 NXChar orientation = root.openNXChar("run/instrument/detector/orientation");
756 // some files have no data there
757 orientation.load();
758
759 if (orientation[0] == 't') {
760 auto p = std::make_unique<Kernel::TimeSeriesProperty<double>>("fromNexus");
761 std::string start_time = root.getString("run/start_time");
762 p->addValue(start_time, -90.0);
763 localWorkspace->mutableRun().addLogData(std::move(p));
764 mainFieldDirection = "Transverse";
765 }
766 } catch (...) {
767 // no data - assume main field was longitudinal
768 }
769
770 // set output property and add to workspace logs
771 auto &run = localWorkspace->mutableRun();
772 setProperty("MainFieldDirection", mainFieldDirection);
773 run.addProperty("main_field_direction", mainFieldDirection);
774
775 ISISRunLogs runLogs(run);
776 runLogs.addStatusLog(run);
777}
778
784void LoadMuonNexus1::addPeriodLog(const DataObjects::Workspace2D_sptr &localWorkspace, int64_t period) {
785 auto &run = localWorkspace->mutableRun();
786 ISISRunLogs runLogs(run);
787 if (period == 0) {
788 runLogs.addPeriodLogs(1, run);
789 } else {
790 run.removeLogData("period 1");
791 runLogs.addPeriodLog(static_cast<int>(period) + 1, run);
792 }
793}
794
795void LoadMuonNexus1::addGoodFrames(const DataObjects::Workspace2D_sptr &localWorkspace, int64_t period, int nperiods) {
796
797 // Get handle to nexus file
798 ::NeXus::File handle(m_filename, NXACC_READ);
799
800 // For single-period datasets, read /run/instrument/beam/frames_good
801 if (nperiods == 1) {
802
803 try {
804
805 handle.openPath("run/instrument/beam");
806 try {
807 handle.openData("frames_good");
808 } catch (::NeXus::Exception &) {
809 // If it's not there, read "frames" instead and assume they are good
810 g_log.warning("Could not read /run/instrument/beam/frames_good");
811 handle.openData("frames");
812 g_log.warning("Using run/instrument/beam/frames instead");
813 }
814
815 // read frames_period_daq
816 boost::scoped_array<int> dataVals(new int[1]);
817 handle.getData(dataVals.get());
818
819 auto &run = localWorkspace->mutableRun();
820 run.addProperty("goodfrm", dataVals[0]);
821
822 } catch (::NeXus::Exception &) {
823 g_log.warning("Could not read number of good frames");
824 }
825
826 } else {
827 // For multi-period datasets, read entries in
828 // /run/instrument/beam/frames_period_daq
829 try {
830
831 handle.openPath("run/instrument/beam/");
832 handle.openData("frames_period_daq");
833
834 ::NeXus::Info info = handle.getInfo();
835 // Check that frames_period_daq contains values for
836 // every period
837 if (period >= info.dims[0]) {
838 std::ostringstream error;
839 error << "goodfrm not found for period " << period;
840 throw std::runtime_error(error.str());
841 }
842 if (nperiods != info.dims[0]) {
843 std::ostringstream error;
844 error << "Inconsistent number of period entries found (";
845 error << info.dims[0];
846 error << "!=" << nperiods << ")";
847 throw std::runtime_error(error.str());
848 }
849
850 // read frames_period_daq
851 boost::scoped_array<int> dataVals(new int[info.dims[0]]);
852 handle.getData(dataVals.get());
853
854 auto &run = localWorkspace->mutableRun();
855 if (period == 0) {
856 // If this is the first period
857 // localWorkspace will not contain goodfrm
858 run.addProperty("goodfrm", dataVals[0]);
859
860 } else {
861 // If period > 0, we need to remove
862 // previous goodfrm log value
863 run.removeLogData("goodfrm");
864 run.addProperty("goodfrm", dataVals[period]);
865 }
866 } catch (::NeXus::Exception &) {
867 g_log.warning("Could not read /run/instrument/beam/frames_period_daq");
868 }
869 } // else
870
871 handle.close();
872}
873
881 const auto &firstEntryNameType = descriptor.firstEntryNameType();
882 const std::string root = "/" + firstEntryNameType.first;
883 if (!descriptor.pathExists(root + "/analysis"))
884 return 0;
885
886 bool upperIDF(true);
887 if (descriptor.pathExists(root + "/IDF_version"))
888 upperIDF = true;
889 else {
890 if (descriptor.pathExists(root + "/idf_version"))
891 upperIDF = false;
892 else
893 return 0;
894 }
895
896 try {
897 std::string versionField = "idf_version";
898 if (upperIDF)
899 versionField = "IDF_version";
900
901 auto &file = descriptor.data();
902 file.openPath(root + "/" + versionField);
903 int32_t version = 0;
904 file.getData(&version);
905 if (version != 1)
906 return 0;
907
908 file.openPath(root + "/analysis");
909 std::string def = file.getStrData();
910 if (def == "muonTD" || def == "pulsedTD") {
911 // If all this succeeded then we'll assume this is an ISIS Muon NeXus file
912 // version 2
913 return 81;
914 }
915 } catch (...) {
916 }
917 return 0;
918}
919
920} // namespace Mantid::DataHandling
double error
Definition: IndexPeaks.cpp:133
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
bool existsProperty(const std::string &name) const override
Checks whether the named property is already in the list of managed property.
Definition: Algorithm.cpp:2008
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
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
GroupingLoader : Loads instrument grouping from IDF file.
std::shared_ptr< Grouping > getDummyGrouping()
Returns a "dummy" grouping of a single group with all the detectors in it.
std::shared_ptr< Grouping > getGroupingFromIDF() const
Load the grouping from the instrument's IDF.
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition: LogManager.h:79
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
This class stores information regarding an experimental run as a series of log entries.
Definition: Run.h:38
A minimal class to hold the mapping between the spectrum number and its related detector ID numbers f...
TableRow represents a row in a TableWorkspace.
Definition: TableRow.h:39
Class to hold a set of workspaces.
Defines a class to aid in creating ISIS specific run logs for periods, status etc.
Definition: ISISRunLogs.h:28
void addPeriodLog(const int period, API::Run &exptRun)
Add 'period i' log.
void addStatusLog(API::Run &exptRun)
Adds the status log to the this run.
Definition: ISISRunLogs.cpp:47
void addPeriodLogs(const int period, API::Run &exptRun)
Adds period related logs.
Definition: ISISRunLogs.cpp:54
void addGoodFrames(const DataObjects::Workspace2D_sptr &localWorkspace, int64_t period, int nperiods)
void exec() override
Overwrites Algorithm method.
API::Workspace_sptr loadDetectorGrouping(Mantid::NeXus::NXRoot &root, const Mantid::Geometry::Instrument_const_sptr &inst)
Loads detector grouping information.
int version() const override
Algorithm's version for identification overriding a virtual method.
DataObjects::TableWorkspace_sptr createDeadTimeTable(std::vector< int > specToLoad, std::vector< double > deadTimes)
Creates Dead Time Table using all the data between begin and end.
void loadRunDetails(const DataObjects::Workspace2D_sptr &localWorkspace)
Log the run details from the file.
int confidence(Kernel::NexusDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
void addPeriodLog(const DataObjects::Workspace2D_sptr &localWorkspace, int64_t period)
Add the 'period i' log to a workspace.
void loadData(size_t hist, specnum_t &i, specnum_t specNo, MuonNexusReader &nxload, const int64_t lengthIn, const DataObjects::Workspace2D_sptr &localWorkspace)
Load in a single spectrum taken from a NeXus file.
void loadDeadTimes(Mantid::NeXus::NXRoot &root)
Loads dead time table for the detector.
void runLoadLog(const DataObjects::Workspace2D_sptr &)
Run the LoadLog Child Algorithm.
DataObjects::TableWorkspace_sptr createDetectorGroupingTable(std::vector< int > specToLoad, std::vector< int > grouping)
Creates Detector Grouping Table using all the data from the range.
It is a base class for loaders for versions 1 and 2 of the muon nexus file format.
Definition: LoadMuonNexus.h:50
std::string m_filename
The name and path of the input file.
Definition: LoadMuonNexus.h:81
specnum_t m_spec_min
The value of the spectrum_min property.
void runLoadInstrument(const DataObjects::Workspace2D_sptr &)
Run the Child Algorithm LoadInstrument.
std::string m_instrument_name
The instrument name from Nexus.
Definition: LoadMuonNexus.h:85
int64_t m_numberOfPeriods
The number of periods in the raw file.
Definition: LoadMuonNexus.h:93
bool m_interval
Have the spectrum_min/max properties been set?
Definition: LoadMuonNexus.h:97
bool m_list
Has the spectrum_list property been set?
Definition: LoadMuonNexus.h:95
specnum_t m_spec_max
The value of the spectrum_max property.
void checkOptionalProperties()
Validates the optional 'spectra to read' properties, if they have been set.
std::vector< specnum_t > m_spec_list
The value of the spectrum_list property.
Definition: LoadMuonNexus.h:99
int64_t m_entrynumber
The number of the input entry.
Definition: LoadMuonNexus.h:89
void addToSampleLog(const std::string &logName, const int logNumber, DataObjects::Workspace2D_sptr &ws)
Function to add a single int as a sample log to a workspace.
specnum_t m_numberOfSpectra
The number of spectra in the raw file.
Definition: LoadMuonNexus.h:91
Records the filename and the description of failure.
Definition: Exception.h:98
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
Defines a wrapper around a file whose internal structure can be accessed using the NeXus API.
inline ::NeXus::File & data()
Access the open NeXus File object.
const std::pair< std::string, std::string > & firstEntryNameType() const
Returns the name & type of the first entry in the file.
bool pathExists(const std::string &path) const
Query if a path exists.
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...
static const UnitLabel Microsecond
Microsecond.
int getInt(const std::string &name) const
Returns a int.
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
Definition: NexusClasses.h:546
bool containsDataSet(const std::string &query) const
Returns whether an individual dataset is present.
NXInfo getDataSetInfo(const std::string &name) const
Returns NXInfo for a dataset.
float getFloat(const std::string &name) const
Returns a float.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
Definition: NexusClasses.h:551
NXChar openNXChar(const std::string &name) const
Creates and opens a char dataset.
Definition: NexusClasses.h:561
std::string getString(const std::string &name) const
Returns a string.
Templated class implementation of NXDataSet.
Definition: NexusClasses.h:203
void load(const int blocksize=1, int i=-1, int j=-1, int k=-1, int l=-1) override
Implementation of the virtual NXDataSet::load(...) method.
Definition: NexusClasses.h:289
int dim0() const
Returns the number of elements along the first dimension.
Implements NXentry Nexus class.
Definition: NexusClasses.h:898
NXAttributes attributes
Attributes.
Definition: NexusClasses.h:111
Implements NXroot Nexus class.
Definition: NexusClasses.h:922
NXEntry openEntry(const std::string &name)
Opens an entry – a topmost Nexus class.
Definition: NexusClasses.h:939
MuunNexusReader opens a Nexus file and reads certain fields expected for a ISIS Muon data file (old f...
std::string m_framesPeriodsRaw
std::string m_periodsOutput
std::string m_framesPeriodsRequested
std::string m_periodNames
void getTimeChannels(float *timebnds, const int &nbnds) const
get time bin boundaries return sample name
int t_nper
number of periods in file (=1 at present)
void readFromFile(const std::string &filename)
read histogram data
std::string m_periodTypes
int t_ntc1
number of time channels in time regime 1
int t_nsp1
number of spectra in time regime 1
std::string getInstrumentName() const
return instrument name
std::string m_periodsCounts
std::vector< int > m_counts
temp store of histogram data
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
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.
MANTID_GEOMETRY_DLL PolygonEdge::Orientation orientation(const PolygonEdge &focusEdge, const PolygonEdge &refEdge, double &t)
Calculate the orientation type of one edge wrt to another.
Definition: PolygonEdge.cpp:81
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
int32_t detid_t
Typedef for a detector ID.
Definition: SpectrumInfo.h:21
int32_t specnum_t
Typedef for a spectrum Number.
Definition: IDTypes.h:16
std::string to_string(const wide_integer< Bits, Signed > &n)
C++ implementation of NeXus classes.
Definition: NexusClasses.h:41
NXstatus stat
return status
Definition: NexusClasses.h:47
int type
type of the data, e.g. NX_CHAR, NX_FLOAT32, see napi.h
Definition: NexusClasses.h:46