Mantid
Loading...
Searching...
No Matches
LoadISISNexus2.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
15
16#include "MantidAPI/Axis.h"
19#include "MantidAPI/Sample.h"
29#include "MantidNexus/NexusFile.h"
30
31#include <algorithm>
32#include <cctype>
33#include <climits>
34#include <cmath>
35#include <sstream>
36#include <vector>
37
38namespace {
41 auto dataBlocks = composite.getDataBlocks();
42 auto monitorBlocks = monitors.getDataBlocks();
43 auto matchesMonitorBlock = [&monitorBlocks](Mantid::DataHandling::DataBlock &dataBlock) {
44 return std::find(std::begin(monitorBlocks), std::end(monitorBlocks), dataBlock) != std::end(monitorBlocks);
45 };
46
48 for (auto &dataBlock : dataBlocks) {
49 if (matchesMonitorBlock(dataBlock)) {
50 newComposite.addDataBlock(dataBlock);
51 }
52 }
53
54 return newComposite;
55}
56} // namespace
57
58namespace Mantid::DataHandling {
59
61
62using namespace Kernel;
63using namespace API;
64using namespace Nexus;
65using namespace HistogramData;
66using std::size_t;
67
70 : m_filename(), m_instrument_name(), m_samplename(), m_detBlockInfo(), m_monBlockInfo(), m_loadBlockInfo(),
71 m_have_detector(false), m_hasVMSBlock(false), m_load_selected_spectra(false), m_wsInd2specNum_map(),
72 m_spec2det_map(), m_entrynumber(0), m_tof_data(), m_spec(), m_spec_end(nullptr), m_monitors(), m_logCreator(),
73 m_progress(), m_nexusFile() {}
74
82 if (descriptor.isEntry("/raw_data_1", "NXentry")) {
83 // It also could be an Event Nexus file or a TOFRaw file,
84 // so confidence is set to less than 80.
85 return 75;
86 }
87 return 0;
88}
89
92 const std::vector<std::string> exts{".nxs", ".n*"};
93 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts),
94 "The name of the Nexus file to load");
95 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output));
96
97 auto mustBePositiveSpectrum = std::make_shared<BoundedValidator<specnum_t>>();
98 mustBePositiveSpectrum->setLower(0);
99 declareProperty("SpectrumMin", static_cast<specnum_t>(0), mustBePositiveSpectrum);
100 declareProperty("SpectrumMax", static_cast<specnum_t>(EMPTY_INT()), mustBePositiveSpectrum);
101 declareProperty(std::make_unique<ArrayProperty<specnum_t>>("SpectrumList"));
102 auto mustBePositive = std::make_shared<BoundedValidator<int64_t>>();
103 declareProperty("EntryNumber", static_cast<int64_t>(0), mustBePositive,
104 "0 indicates that every entry is loaded, into a separate "
105 "workspace within a group. "
106 "A positive number identifies one entry to be loaded, into "
107 "one worskspace");
108
109 std::vector<std::string> monitorOptions{"Include", "Exclude", "Separate"};
110 std::map<std::string, std::string> monitorOptionsAliases;
111 monitorOptionsAliases["1"] = "Separate";
112 monitorOptionsAliases["0"] = "Exclude";
113 declareProperty("LoadMonitors", "Include",
114 std::make_shared<Kernel::StringListValidator>(monitorOptions, monitorOptionsAliases),
115 "Option to control the loading of monitors.\n"
116 "Allowed options are Include,Exclude, Separate.\n"
117 "Include:The default is Include option would load monitors with the "
118 "workspace if monitors spectra are within the range of loaded "
119 "detectors.\n"
120 "If the time binning for the monitors is different from the\n"
121 "binning of the detectors this option is equivalent to the Separate "
122 "option\n"
123 "Exclude:Exclude option excludes monitors from the output workspace.\n"
124 "Separate:Separate option loads monitors into a separate workspace "
125 "called: OutputWorkspace_monitors.\n"
126 "Defined aliases:\n"
127 "1: Equivalent to Separate.\n"
128 "0: Equivalent to Exclude.\n");
129}
130
139
140 //**********************************************************************
141 // process load monitor options request
142 bool bincludeMonitors, bseparateMonitors, bexcludeMonitors;
143 LoadRawHelper::ProcessLoadMonitorOptions(bincludeMonitors, bseparateMonitors, bexcludeMonitors, this);
144
145 //**********************************************************************
146 m_filename = getPropertyValue("Filename");
147 // Create the root Nexus class
148 NXRoot root(m_filename);
149
150 // "Open" the same file but with the C++ interface
151 m_nexusFile.reset(new Nexus::File(root.m_fileID));
152
153 // Open the raw data group 'raw_data_1'
154 NXEntry entry = root.openEntry("raw_data_1");
155
156 // Read in the instrument name from the Nexus file
157 m_instrument_name = entry.getString("name");
158
159 // Test if we have a vms block
160 if (entry.containsGroup("isis_vms_compat")) {
161 m_hasVMSBlock = true;
162 }
163
164 // Get number of detectors and spectrum list
165 size_t ndets{0};
166 try {
167 NXClass det_class = entry.openNXGroup("detector_1");
168 NXInt spectrum_index = det_class.openNXInt("spectrum_index");
169 spectrum_index.load();
170 ndets = spectrum_index.dim0();
171 // We assume that this spectrum list increases monotonically
172 m_spec = spectrum_index.vecBuffer();
173 m_spec_end = m_spec.data() + ndets;
174 m_have_detector = true;
175 } catch (std::runtime_error &) {
176 ndets = 0;
177 }
178
179 // Load detector and spectra ids, and number of monitors + detectors?
182
183 // Pull out the monitor blocks, if any exist
184 size_t nmons{0};
185
186 for (auto it = entry.groups().cbegin(); it != entry.groups().cend(); ++it) {
187 if (it->nxclass == "NXmonitor") // Count monitors
188 {
189 NXInt index = entry.openNXInt(std::string(it->nxname) + "/spectrum_index");
190 index.load();
191 specnum_t ind = static_cast<specnum_t>(*index());
192 // Spectrum index of 0 means no spectrum associated with that monitor,
193 // so only count those with index > 0
194 if (ind > 0) {
195 m_monitors[ind] = it->nxname;
196 ++nmons;
197 }
198 }
199 }
200
201 if (ndets == 0 && nmons == 0) {
202 if (bexcludeMonitors) {
203 g_log.warning() << "Nothing to do. No detectors found and no monitor "
204 "loading requested";
205 return;
206 } else {
207 g_log.error() << "Invalid NeXus structure, cannot find detector or monitor blocks.";
208 throw std::runtime_error("Inconsistent NeXus file structure.");
209 }
210 }
211
212 // Determine the data block for the detectors and monitors
213 bseparateMonitors =
214 findSpectraDetRangeInFile(entry, m_spec, ndets, nsp1, m_monitors, bexcludeMonitors, bseparateMonitors);
215
216 size_t x_length = m_loadBlockInfo.getNumberOfChannels() + 1;
217
218 // Check input is consistent with the file, throwing if not
219 bseparateMonitors = checkOptionalProperties(bseparateMonitors, bexcludeMonitors);
220 // Fill up m_spectraBlocks
221 size_t total_specs = prepareSpectraBlocks(m_monitors, m_loadBlockInfo);
222
223 m_progress = std::make_shared<API::Progress>(this, 0.0, 1.0, total_specs * m_detBlockInfo.getNumberOfPeriods());
224
225 DataObjects::Workspace2D_sptr local_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
226 WorkspaceFactory::Instance().create("Workspace2D", total_specs, x_length, m_loadBlockInfo.getNumberOfChannels()));
227 // Set the units on the workspace to TOF & Counts
228 local_workspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
229 local_workspace->setYUnit("Counts");
230
231 // Load instrument and other data once then copy it later
232 m_progress->report("Loading instrument and run details");
233
234 // load run details
235 loadRunDetails(local_workspace, entry);
236
237 // Test if IDF exists in Nexus otherwise load default instrument
238 bool foundInstrument = LoadEventNexus::runLoadIDFFromNexus(m_filename, local_workspace, "raw_data_1", this);
240 m_spec2det_map = SpectrumDetectorMapping(spec(), udet(), udet.dim0());
241 else if (bseparateMonitors) {
242 m_spec2det_map = SpectrumDetectorMapping(spec(), udet(), udet.dim0());
243 local_workspace->updateSpectraUsing(m_spec2det_map);
244 } else {
245 local_workspace->updateSpectraUsing(SpectrumDetectorMapping(spec(), udet(), udet.dim0()));
246 }
247
248 if (!foundInstrument) {
249 runLoadInstrument(local_workspace);
250 }
251
252 // Load logs and sample information
253 m_nexusFile->openAddress(entry.address());
254 local_workspace->loadSampleAndLogInfoNexus(m_nexusFile.get());
255
256 // Load logs and sample information further information... See maintenance
257 // ticket #8697
258 loadSampleData(local_workspace, entry);
259 m_progress->report("Loading logs");
260 loadLogs(local_workspace);
261
262 // Load first period outside loop
263 m_progress->report("Loading data");
264 // Get X Data
265 if (ndets > 0) {
267 }
268
269 int64_t firstentry = (m_entrynumber > 0) ? m_entrynumber : 1;
270 loadPeriodData(firstentry, entry, local_workspace, m_load_selected_spectra);
271
272 // Clone the workspace at this point to provide a base object for future
273 // workspace generation.
274 DataObjects::Workspace2D_sptr period_free_workspace =
275 std::dynamic_pointer_cast<DataObjects::Workspace2D>(WorkspaceFactory::Instance().create(local_workspace));
276
277 createPeriodLogs(firstentry, local_workspace);
278
279 WorkspaceGroup_sptr wksp_group(new WorkspaceGroup);
281 wksp_group->setTitle(local_workspace->getTitle());
282
283 // This forms the name of the group
284 const std::string base_name = getPropertyValue("OutputWorkspace") + "_";
285 const std::string prop_name = "OutputWorkspace_";
286
287 for (std::size_t p = 1; p <= m_loadBlockInfo.getNumberOfPeriods(); ++p) {
288 std::ostringstream os;
289 os << p;
290 m_progress->report("Loading period " + os.str());
291 if (p > 1) {
292 local_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
293 WorkspaceFactory::Instance().create(period_free_workspace));
294 loadPeriodData(p, entry, local_workspace, m_load_selected_spectra);
295 createPeriodLogs(p, local_workspace);
296 // Check consistency of logs data for multi-period workspaces and raise
297 // warnings where necessary.
298 validateMultiPeriodLogs(local_workspace);
299 }
300 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>(prop_name + os.str(), base_name + os.str(),
302 wksp_group->addWorkspace(local_workspace);
303 setProperty(prop_name + os.str(), std::static_pointer_cast<Workspace>(local_workspace));
304 }
305 // The group is the root property value
306 setProperty("OutputWorkspace", std::dynamic_pointer_cast<Workspace>(wksp_group));
307 } else {
308 setProperty("OutputWorkspace", std::dynamic_pointer_cast<Workspace>(local_workspace));
309 }
310
311 //***************************************************************************************************
312 // Workspace or group of workspaces without monitors is loaded. Now we are
313 // loading monitors separately.
314 if (bseparateMonitors) {
315 std::string wsName = getPropertyValue("OutputWorkspace");
317 x_length = m_monBlockInfo.getNumberOfChannels() + 1;
318 // reset the size of the period free workspace to the monitor size
319 period_free_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(WorkspaceFactory::Instance().create(
320 period_free_workspace, m_monBlockInfo.getNumberOfSpectra(), x_length, m_monBlockInfo.getNumberOfChannels()));
321 auto monitor_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
322 WorkspaceFactory::Instance().create(period_free_workspace));
323
324 m_spectraBlocks.clear();
325 m_wsInd2specNum_map.clear();
326 // at the moment here we clear this map to enable possibility to load
327 // monitors from the spectra block (wiring table bug).
328 // if monitor's spectra present in the detectors block due to this bug
329 // should be read from monitors, this map should be dealt with properly.
330 buildSpectraInd2SpectraNumMap(true /*hasRange*/, false /*hasSpectraList*/, m_monBlockInfo);
331
332 // lo
334
335 firstentry = (m_entrynumber > 0) ? m_entrynumber : 1;
336 loadPeriodData(firstentry, entry, monitor_workspace, true);
337 local_workspace->setMonitorWorkspace(monitor_workspace);
338
339 ISISRunLogs monLogCreator(monitor_workspace->run());
340 monLogCreator.addPeriodLogs(1, monitor_workspace->mutableRun());
341
342 const std::string monitorPropBase = "MonitorWorkspace";
343 const std::string monitorWsNameBase = wsName + "_monitors";
345 WorkspaceGroup_sptr monitor_group(new WorkspaceGroup);
346 monitor_group->setTitle(monitor_workspace->getTitle());
347
348 for (std::size_t p = 1; p <= m_detBlockInfo.getNumberOfPeriods(); ++p) {
349 std::ostringstream os;
350 os << "_" << p;
351 m_progress->report("Loading period " + os.str());
352 if (p > 1) {
353 monitor_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
354 WorkspaceFactory::Instance().create(period_free_workspace));
355 loadPeriodData(p, entry, monitor_workspace, m_load_selected_spectra);
356 monLogCreator.addPeriodLogs(static_cast<int>(p), monitor_workspace->mutableRun());
357 // Check consistency of logs data for multi-period workspaces and
358 // raise
359 // warnings where necessary.
360 validateMultiPeriodLogs(monitor_workspace);
361 auto data_ws = std::static_pointer_cast<API::MatrixWorkspace>(wksp_group->getItem(p - 1));
362 data_ws->setMonitorWorkspace(monitor_workspace);
363 }
365 monitorPropBase + os.str(), monitorWsNameBase + os.str(), Direction::Output));
366 monitor_group->addWorkspace(monitor_workspace);
367 setProperty(monitorPropBase + os.str(), std::static_pointer_cast<Workspace>(monitor_workspace));
368 }
369 // The group is the root property value
371 std::make_unique<WorkspaceProperty<Workspace>>(monitorPropBase, monitorWsNameBase, Direction::Output));
372 setProperty(monitorPropBase, std::dynamic_pointer_cast<Workspace>(monitor_group));
373
374 } else {
376 std::make_unique<WorkspaceProperty<Workspace>>(monitorPropBase, monitorWsNameBase, Direction::Output));
377 setProperty(monitorPropBase, std::static_pointer_cast<Workspace>(monitor_workspace));
378 }
379 } else {
380 g_log.information() << " no monitors to load for workspace: " << wsName << '\n';
381 }
382 }
383
384 // Clear off the member variable containers
385 m_tof_data.reset();
386 m_spec.clear();
387 m_monitors.clear();
388 m_wsInd2specNum_map.clear();
389 m_nexusFile->close();
390}
396 const Run &run = ws->run();
397 if (!run.hasProperty("current_period")) {
398 g_log.warning("Workspace has no current_period log.");
399 }
400 if (!run.hasProperty("nperiods")) {
401 g_log.warning("Workspace has no nperiods log");
402 }
403 if (!run.hasProperty("proton_charge_by_period")) {
404 g_log.warning("Workspace has not proton_charge_by_period log");
405 }
406}
407
415bool LoadISISNexus2::checkOptionalProperties(bool bseparateMonitors, bool bexcludeMonitor) {
416 // optional properties specify that only some spectra have to be loaded
417 bool range_supplied(false);
418
419 // Get the spectrum selection which were specfied by the user
420 specnum_t spec_min = getProperty("SpectrumMin");
421 specnum_t spec_max = getProperty("SpectrumMax");
422
423 // If spearate monitors or excluded monitors is selected then we
424 // need to build up a wsIndex to spectrum number map as well,
425 // since we cannot rely on contiguous blocks of detectors
426 if (bexcludeMonitor || bseparateMonitors) {
428 }
429
430 if (spec_min == 0)
432 else {
433 range_supplied = true;
435 }
436
437 if (spec_max == EMPTY_INT())
439 else {
440 range_supplied = true;
442 }
443
444 // Sanity check for min/max
445 if (spec_min > spec_max) {
446 throw std::invalid_argument("Inconsistent range properties. SpectrumMin is "
447 "larger than SpectrumMax.");
448 }
449
450 if (spec_max > m_loadBlockInfo.getMaxSpectrumID()) {
451 std::string err = "Inconsistent range property. SpectrumMax is larger than number of "
452 "spectra: " +
454 throw std::invalid_argument(err);
455 }
456
457 // Check the entry number
458 m_entrynumber = getProperty("EntryNumber");
459 if (static_cast<size_t>(m_entrynumber) > m_loadBlockInfo.getNumberOfPeriods()) {
460 std::string err = "Invalid entry number entered. File contains " +
462 throw std::invalid_argument(err);
463 }
464
466 m_entrynumber = 1;
467 }
468
469 // Did the user provide a spectrum list
470 std::vector<specnum_t> spec_list = getProperty("SpectrumList");
471 auto hasSpecList = false;
472
473 if (!spec_list.empty()) {
475
476 // Sort the list so that we can check it's range
477 std::sort(spec_list.begin(), spec_list.end());
478
479 // Check if the spectra list entries are outside of the bounds
480 // If we load the monitors separately, then we need to make sure that we
481 // take them into account
482 bool isSpectraListTooLarge;
483 bool isSpectraListTooSmall;
484 auto maxLoadBlock = m_loadBlockInfo.getMaxSpectrumID();
485 auto minLoadBlock = m_loadBlockInfo.getMinSpectrumID();
486 if (bseparateMonitors) {
487 auto maxMonBlock = m_monBlockInfo.getMaxSpectrumID();
488 auto minMonBlock = m_monBlockInfo.getMinSpectrumID();
489 isSpectraListTooLarge = spec_list.back() > std::max(maxMonBlock, maxLoadBlock);
490 isSpectraListTooSmall = spec_list.front() < std::min(minMonBlock, minLoadBlock);
491
492 } else {
493 isSpectraListTooLarge = spec_list.back() > maxLoadBlock;
494 isSpectraListTooSmall = spec_list.front() < minLoadBlock;
495 }
496
497 if (isSpectraListTooLarge) {
498 std::string err = "The specified spectrum list contains a spectrum number which is "
499 "larger "
500 "than the largest loadable spectrum number for your selection of "
501 "excluded/included/separate monitors. The largest loadable "
502 "spectrum number is " +
504 throw std::invalid_argument(err);
505 }
506 if (isSpectraListTooSmall) {
507 std::string err = "The specified spectrum list contains a spectrum number which is "
508 "smaller "
509 "than the smallest loadable spectrum number for your selection of "
510 "excluded/included/separate monitors. The smallest loadable "
511 "spectrum number is " +
513 throw std::invalid_argument(err);
514 }
515
516 // The users can provide a spectrum list and and a spectrum range. Handle
517 // this here.
518 if (range_supplied) {
519 // First remove all entries which are inside of the min and max spectrum,
520 // to avoid duplicates
521 auto isInRange = [&spec_min, &spec_max](specnum_t x) { return (spec_min <= x) && (x <= spec_max); };
522
523 spec_list.erase(remove_if(spec_list.begin(), spec_list.end(), isInRange), spec_list.end());
524
525 // The spec_min - spec_max range needs to be added to the spec list
526 for (auto i = spec_min; i < spec_max + 1; ++i) {
527 spec_list.emplace_back(i);
528 }
529 std::sort(spec_list.begin(), spec_list.end());
530 }
531
532 auto monitorSpectra = m_monBlockInfo.getAllSpectrumNumbers();
533 // Create DataBlocks from the spectrum list
534 DataBlockComposite composite;
535 populateDataBlockCompositeWithContainer(composite, spec_list, spec_list.size(),
537 monitorSpectra);
538
539 // If the monitors are to be loaded separately, then we have
540 // to remove them at this point, but we also have to check if the
541 if (bexcludeMonitor || bseparateMonitors) {
542 auto newMonitors = getMonitorsFromComposite(composite, m_monBlockInfo);
543 composite.removeSpectra(m_monBlockInfo);
544
545 // This is important. If there are no monitors which were specifically
546 // selected,
547 // then we load the full monitor range, else respect the selection.
548 if (!newMonitors.isEmpty()) {
549 m_monBlockInfo = newMonitors;
550 }
551
552 // Handle case where the composite is empty since it only contained
553 // monitors, but we want to load the monitors sepearately. In this case we
554 // should set the loadBlock to the selected monitors.
555 if (bseparateMonitors && composite.isEmpty()) {
556 composite = m_monBlockInfo;
557 bseparateMonitors = false;
558 }
559 }
560
561 m_loadBlockInfo = composite;
562
563 hasSpecList = true;
564 } else {
565 // At this point we don't have a spectrum list but there might have been a
566 // spectrum range which we need to take into account, by truncating
567 // the current range. If we load the monitors separately, we need to
568 // truncate them as well (provided they are affected)
569 if (range_supplied) {
570 m_loadBlockInfo.truncate(spec_min, spec_max);
571
572 auto new_monitors = m_monBlockInfo;
573 new_monitors.truncate(spec_min, spec_max);
574 m_monBlockInfo = new_monitors;
575 }
576 }
577
579 buildSpectraInd2SpectraNumMap(range_supplied, hasSpecList, m_loadBlockInfo);
580 }
581
582 // Check that the load blocks contain anything at all.
583 if (m_loadBlockInfo.isEmpty()) {
584 throw std::invalid_argument("Your spectrum number selection was not valid. "
585 "Make sure that you select spectrum numbers "
586 "and ranges which are compatible with your "
587 "selection of excluded/included/separate monitors. ");
588 }
589
590 return bseparateMonitors;
591}
592
603void LoadISISNexus2::buildSpectraInd2SpectraNumMap(bool range_supplied, bool hasSpectraList,
604 const DataBlockComposite &dataBlockComposite) {
605
606 if (range_supplied || hasSpectraList || true) {
607 auto generator = dataBlockComposite.getGenerator();
608 int64_t hist = 0;
609 for (; !generator->isDone(); generator->next()) {
610 auto spec_num = static_cast<specnum_t>(generator->getValue());
611 m_wsInd2specNum_map.emplace(hist, spec_num);
612 ++hist;
613 }
614 }
615}
616
623size_t LoadISISNexus2::prepareSpectraBlocks(std::map<specnum_t, std::string> &monitors, DataBlockComposite &LoadBlock) {
624 std::vector<specnum_t> includedMonitors;
625 // Setup the SpectraBlocks based on the DataBlocks
626 auto dataBlocks = LoadBlock.getDataBlocks();
627 auto isMonitor = [&monitors](specnum_t spectrumNumber) { return monitors.find(spectrumNumber) != monitors.end(); };
628 for (const auto &dataBlock : dataBlocks) {
629 auto min = dataBlock.getMinSpectrumID();
630 if (isMonitor(min)) {
631 m_spectraBlocks.emplace_back(SpectraBlock(min, min, true, monitors.find(min)->second));
632 includedMonitors.emplace_back(min);
633 } else {
634 auto max = dataBlock.getMaxSpectrumID();
635 m_spectraBlocks.emplace_back(min, max, false, "");
636 }
637 }
638
639 // sort and check for overlapping
640 if (m_spectraBlocks.size() > 1) {
641 std::sort(m_spectraBlocks.begin(), m_spectraBlocks.end(),
642 [](const LoadISISNexus2::SpectraBlock &block1, const LoadISISNexus2::SpectraBlock &block2) {
643 return block1.last < block2.first;
644 });
646 }
647
648 // Remove monitors that have been used.
649 auto allMonitorsIncluded = monitors.size() == includedMonitors.size();
650 if (!includedMonitors.empty() && !allMonitorsIncluded) {
651 for (auto it = monitors.begin(); it != monitors.end();) {
652 if (std::find(includedMonitors.begin(), includedMonitors.end(), it->first) != includedMonitors.end()) {
653 auto it1 = it;
654 ++it;
655 monitors.erase(it1);
656 } else {
657 ++it;
658 }
659 }
660 }
661
662 // Count the number of spectra.
663 const auto nSpec = std::accumulate(
664 m_spectraBlocks.cbegin(), m_spectraBlocks.cend(), static_cast<size_t>(0),
665 [](size_t sum, const auto &spectraBlock) { return sum + spectraBlock.last - spectraBlock.first + 1; });
666 return nSpec;
667}
668
677 for (size_t i = 1; i < m_spectraBlocks.size(); ++i) {
678 const auto &block1 = m_spectraBlocks[i - 1];
679 const auto &block2 = m_spectraBlocks[i];
680 if (block1.first > block1.last && block2.first > block2.last)
681 throw std::runtime_error("LoadISISNexus2: inconsistent spectra ranges");
682 if (block1.last >= block2.first) {
683 throw std::runtime_error("LoadISISNexus2: the range of SpectraBlocks must not overlap");
684 }
685 }
686}
687
697void LoadISISNexus2::loadPeriodData(int64_t period, NXEntry &entry, DataObjects::Workspace2D_sptr &local_workspace,
698 bool update_spectra2det_mapping) {
699 int64_t hist_index = 0;
700 int64_t period_index(period - 1);
701
702 for (const auto &spectraBlock : m_spectraBlocks) {
703 if (spectraBlock.isMonitor) {
704 NXData monitor = entry.openNXData(spectraBlock.monName);
705 NXInt mondata = monitor.openIntData();
706 m_progress->report("Loading monitor");
707 mondata.load(1, static_cast<int>(period - 1)); // TODO this is just wrong
708 NXFloat timeBins = monitor.openNXFloat("time_of_flight");
709 timeBins.load();
710 local_workspace->setHistogram(hist_index, BinEdges(timeBins(), timeBins() + timeBins.dim0()),
711 Counts(mondata(), mondata() + m_monBlockInfo.getNumberOfChannels()));
712
713 if (update_spectra2det_mapping) {
714 auto &spec = local_workspace->getSpectrum(hist_index);
715 specnum_t specNum = m_wsInd2specNum_map.at(hist_index);
716 spec.setDetectorIDs(m_spec2det_map.getDetectorIDsForSpectrumNo(specNum));
717 spec.setSpectrumNo(specNum);
718 }
719 hist_index++;
720 } else if (m_have_detector) {
721 NXData nxdata = entry.openNXData("detector_1");
722 NXInt data = nxdata.openIntData();
723 data.open();
724 // Start with the list members that are lower than the required spectrum
725 const int *const spec_begin = m_spec.data();
726 // When reading in blocks we need to be careful that the range is exactly
727 // divisible by the block-size
728 // and if not have an extra read of the left overs
729 const int64_t blocksize = 8;
730 const int64_t rangesize = spectraBlock.last - spectraBlock.first + 1;
731 const int64_t fullblocks = rangesize / blocksize;
732 int64_t spectra_no = spectraBlock.first;
733
734 // For this to work correctly, we assume that the spectrum list increases
735 // monotonically
736 int64_t filestart = std::lower_bound(spec_begin, m_spec_end, spectra_no) - spec_begin;
737 if (fullblocks > 0) {
738 for (int64_t i = 0; i < fullblocks; ++i) {
739 loadBlock(data, blocksize, period_index, filestart, hist_index, spectra_no, local_workspace);
740 filestart += blocksize;
741 }
742 }
743 int64_t finalblock = rangesize - (fullblocks * blocksize);
744 if (finalblock > 0) {
745 loadBlock(data, finalblock, period_index, filestart, hist_index, spectra_no, local_workspace);
746 }
747 }
748 }
749
750 try {
751 const std::string title = entry.getString("title");
752 local_workspace->setTitle(title);
753 // write the title into the log file (run object)
754 local_workspace->mutableRun().addProperty("run_title", title, true);
755 } catch (std::runtime_error &) {
756 g_log.debug() << "No title was found in the input file, " << getPropertyValue("Filename") << '\n';
757 }
758
759 std::string notes = "";
760 try {
761 notes = entry.getString("notes");
762 } catch (std::runtime_error &) {
763 // if no notes, add empty string
764 }
765 local_workspace->setComment(notes);
766}
767
774 m_logCreator->addPeriodLogs(static_cast<int>(period), local_workspace->mutableRun());
775}
776
788void LoadISISNexus2::loadBlock(NXInt &data, int64_t blocksize, int64_t period, int64_t start, int64_t &hist,
789 int64_t &spec_num, DataObjects::Workspace2D_sptr &local_workspace) {
790 data.load(blocksize, period, start); // TODO this is just wrong
791 int *data_start = data();
792 int *data_end = data_start + m_loadBlockInfo.getNumberOfChannels();
793 int64_t final(hist + blocksize);
794 while (hist < final) {
795 m_progress->report("Loading data");
796 local_workspace->setHistogram(hist, BinEdges(m_tof_data), Counts(data_start, data_end));
797 data_start += m_detBlockInfo.getNumberOfChannels();
800 auto &spec = local_workspace->getSpectrum(hist);
801 specnum_t specNum = m_wsInd2specNum_map.at(hist);
802 // set detectors corresponding to spectra Number
803 spec.setDetectorIDs(m_spec2det_map.getDetectorIDsForSpectrumNo(specNum));
804 // set correct spectra Number
805 spec.setSpectrumNo(specNum);
806 }
807
808 ++hist;
809 ++spec_num;
810 }
811}
812
815
816 auto loadInst = createChildAlgorithm("LoadInstrument");
817
818 // Now execute the Child Algorithm. Catch and log any error, but don't stop.
819 bool executionSuccessful(true);
820 try {
821 loadInst->setPropertyValue("InstrumentName", m_instrument_name);
822 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
823 loadInst->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(false));
824 loadInst->execute();
825 } catch (std::invalid_argument &) {
826 g_log.information("Invalid argument to LoadInstrument Child Algorithm");
827 executionSuccessful = false;
828 } catch (std::runtime_error &) {
829 g_log.information("Unable to successfully run LoadInstrument Child Algorithm");
830 executionSuccessful = false;
831 }
832 if (executionSuccessful) {
833 // If requested update the instrument to positions in the data file
834 const auto &pmap = localWorkspace->constInstrumentParameters();
835 if (pmap.contains(localWorkspace->getInstrument()->getComponentID(), "det-pos-source")) {
836 std::shared_ptr<Geometry::Parameter> updateDets =
837 pmap.get(localWorkspace->getInstrument()->getComponentID(), "det-pos-source");
838 std::string value = updateDets->value<std::string>();
839 if (value.substr(0, 8) == "datafile") {
840 auto updateInst = createChildAlgorithm("UpdateInstrumentFromFile");
841 updateInst->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
842 updateInst->setPropertyValue("Filename", m_filename);
843 if (value == "datafile-ignore-phi") {
844 updateInst->setProperty("IgnorePhi", true);
845 g_log.information("Detector positions in IDF updated with positions "
846 "in the data file except for the phi values");
847 } else {
848 g_log.information("Detector positions in IDF updated with positions "
849 "in the data file");
850 }
851 // We want this to throw if it fails to warn the user that the
852 // information is not correct.
853 updateInst->execute();
854 }
855 }
856 }
857}
858
865
866 API::Run &runDetails = local_workspace->mutableRun();
867
868 // Data details on run not the workspace
869 runDetails.addProperty("nspectra", static_cast<int>(m_loadBlockInfo.getNumberOfSpectra()));
870 runDetails.addProperty("nchannels", static_cast<int>(m_loadBlockInfo.getNumberOfChannels()));
871 runDetails.addProperty("nperiods", static_cast<int>(m_loadBlockInfo.getNumberOfPeriods()));
872
874}
875
882
883 // load sample geometry - Id and dimensions
884 LoadISISNexusHelper::loadSampleGeometry(local_workspace->mutableSample(), entry, m_hasVMSBlock);
885 g_log.debug() << "Sample geometry - ID: " << local_workspace->mutableSample().getGeometryFlag()
886 << ", thickness: " << local_workspace->mutableSample().getThickness()
887 << ", height: " << local_workspace->mutableSample().getHeight()
888 << ", width: " << local_workspace->mutableSample().getWidth() << "\n";
889}
890
897 auto alg = createChildAlgorithm("LoadNexusLogs", 0.0, 0.5);
898 alg->setPropertyValue("Filename", this->getProperty("Filename"));
899 alg->setProperty<MatrixWorkspace_sptr>("Workspace", ws);
900 try {
901 alg->executeAsChildAlg();
902 } catch (std::runtime_error &) {
903 g_log.warning() << "Unable to load run logs. There will be no log "
904 << "data associated with this workspace\n";
905 return;
906 }
907
908 // Populate the instrument parameters.
909 ws->populateInstrumentParameters();
910
911 // Make log creator object and add the run status log
912 m_logCreator.reset(new API::ISISRunLogs(ws->run()));
913 m_logCreator->addStatusLog(ws->mutableRun());
914}
915
916double LoadISISNexus2::dblSqrt(double in) { return sqrt(in); }
942bool LoadISISNexus2::findSpectraDetRangeInFile(const NXEntry &entry, std::vector<specnum_t> &spectrum_index,
943 int64_t ndets, int64_t n_vms_compat_spectra,
944 const std::map<specnum_t, std::string> &monitors, bool excludeMonitors,
945 bool separateMonitors) {
946 size_t nmons = monitors.size();
947
948 if (nmons > 0) {
949 NXInt chans = entry.openNXInt(m_monitors.begin()->second + "/data");
950
951 // Iterate over each monitor and create a data block for each monitor
952 for (const auto &monitor : monitors) {
953 auto monID = monitor.first;
954 auto monTemp = DataBlock(chans);
955 monTemp.setMinSpectrumID(monID);
956 monTemp.setMaxSpectrumID(monID);
958 }
959
960 // at this stage we assume that the only going to load monitors
962 }
963
964 // Check if the there are only monitors in the workspace, in which
965 // case the monitors are loaded as the main workspace, ie not as
966 // a separate workspace.
967 if (ndets == 0) {
968 separateMonitors = false;
969 return separateMonitors;
970 }
971
972 // There are detectors present. The spectrum_index array contains
973 // all available spectra of detectors, but these indices might
974 // not be contiguous.
975 NXData nxData = entry.openNXData("detector_1");
976 NXInt data = nxData.openIntData();
977
978 auto monitorSpectra = m_monBlockInfo.getAllSpectrumNumbers();
980 static_cast<int>(data.dim0()) /*Number of Periods*/,
981 data.dim2() /*Number of channels*/, std::move(monitorSpectra));
982
983 // We should handle legacy files which include the spectrum number of the
984 // monitors
985 // in the detector group ("raw_data_1/detector_1/spectrum_index")
986 // Simple try to remove the monitors. If they are not included nothing should
987 // happen
989
991
992 // Check what is actually going or can be loaded
993 bool removeMonitors = excludeMonitors || separateMonitors;
994
995 // If the monitors are to be pulled into the same workspace as the detector
996 // information,
997 // then the number of periods and the number of channels has to conincide
1000 if (!removeMonitors) {
1001 g_log.warning() << " Performing separate loading as can not load spectra "
1002 "and monitors in the single workspace:\n";
1003 g_log.warning() << " Monitors data contain :" << m_monBlockInfo.getNumberOfChannels()
1004 << " time channels and: " << m_monBlockInfo.getNumberOfPeriods() << " period(s)\n";
1005 g_log.warning() << " Spectra data contain :" << m_detBlockInfo.getNumberOfChannels()
1006 << " time channels and: " << m_detBlockInfo.getNumberOfPeriods() << " period(s)\n";
1007 }
1008
1009 // Force the monitors to be removed and separate if the periods and channels
1010 // don't conincide
1011 // between monitors and detectors.
1012 separateMonitors = true;
1013 removeMonitors = true;
1014 }
1015
1016 int64_t spectraID_min = std::min(m_monBlockInfo.getMinSpectrumID(), m_detBlockInfo.getMinSpectrumID());
1017 int64_t spectraID_max = std::max(m_monBlockInfo.getMaxSpectrumID(), m_detBlockInfo.getMaxSpectrumID());
1019
1020 // In case we want to load everything into a one workspace, we should combine
1021 // the
1022 // the data blocks of the monitor and the detector
1023 if (!removeMonitors) {
1025 }
1026
1027 // If the monitors are to be loaded separately, then we set the loadblocks to
1028 // the detblocks,
1029 // since we want to deal with the detectors (the main workspace) first.
1030 if (separateMonitors)
1032
1033 // Perform a sanity check of the spectrum numbers
1034 if ((totNumOfSpectra != static_cast<size_t>(n_vms_compat_spectra)) ||
1035 (spectraID_max - spectraID_min + 1 != static_cast<int64_t>(n_vms_compat_spectra))) {
1036 // At this point we normally throw since there is a mismatch between the
1037 // number
1038 // spectra of the detectors+monitors and the entry in NSP1, but in the
1039 // case of multiple time regimes this comparison is not any longer valid.
1040 // Hence we only throw if the file does not correspond to a multiple time
1041 // regime file.
1042 if (!isMultipleTimeRegimeFile(entry)) {
1043 throw std::runtime_error("LoadISISNexus: There seems to be an "
1044 "inconsistency in the spectrum numbers.");
1045 }
1046 }
1047
1048 return separateMonitors;
1049}
1050
1059 auto hasMultipleTimeRegimes(false);
1060 try {
1061 NXClass instrument = entry.openNXGroup("instrument");
1062 NXClass dae = instrument.openNXGroup("dae");
1063 hasMultipleTimeRegimes = dae.containsGroup("time_channels_3");
1064 } catch (...) {
1065 }
1066 return hasMultipleTimeRegimes;
1067}
1068
1069} // namespace Mantid::DataHandling
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
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
@ Load
allowed here which will be passed to the algorithm
Defines a class to aid in creating ISIS specific run logs for periods, status etc.
Definition ISISRunLogs.h:28
void addPeriodLogs(const int period, API::Run &exptRun)
Adds period related logs.
bool hasProperty(const std::string &name) const
Does the property exist on the object.
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
A minimal class to hold the mapping between the spectrum number and its related detector ID numbers f...
const std::set< detid_t > & getDetectorIDsForSpectrumNo(const specnum_t spectrumNo) const
Class to hold a set of workspaces.
A property class for workspaces.
DataBlockComposite: The DataBlockComposite handles a collection of DataBlocks.
void truncate(specnum_t specMin, specnum_t specMax)
std::vector< specnum_t > getAllSpectrumNumbers()
Provides a container with all spectrum numbers.
std::unique_ptr< DataBlockGenerator > getGenerator() const override
void addDataBlock(const DataBlock &dataBlock)
void removeSpectra(DataBlockComposite &toRemove)
Removes the input data blocks from the current list of data blocks.
DataBlock: The DataBlock class holds information about a contiguous block of spectrum numbers.
Definition DataBlock.h:28
static bool runLoadIDFFromNexus(const std::string &nexusfilename, T localWorkspace, const std::string &top_entry_name, Algorithm *alg)
Load instrument for Nexus file.
int64_t m_entrynumber
The number of the input entry.
std::string m_filename
The name and path of the input file.
void buildSpectraInd2SpectraNumMap(bool range_supplied, bool hasSpectraList, const DataBlockComposite &dataBlockComposite)
Build the list of spectra to load and include into spectra-detectors map.
const specnum_t * m_spec_end
Pointer to one-past-the-end of spectrum number array (m_spec)
void createPeriodLogs(int64_t period, DataObjects::Workspace2D_sptr &local_workspace)
Creates period log data in the workspace.
std::map< int64_t, specnum_t > m_wsInd2specNum_map
map of workspace Index to spectra Number (spectraID)
int confidence(Nexus::NexusDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
void loadLogs(DataObjects::Workspace2D_sptr &ws)
Load log data from the nexus file.
void exec() override
Overwrites Algorithm method.
bool findSpectraDetRangeInFile(const Nexus::NXEntry &entry, std::vector< specnum_t > &spectrum_index, int64_t ndets, int64_t n_vms_compat_spectra, const std::map< specnum_t, std::string > &monitors, bool excludeMonitors, bool separateMonitors)
Method takes input parameters which describe monitor loading and analyze them against spectra/monitor...
void checkOverlappingSpectraRange()
Check if any of the spectra block ranges overlap.
std::map< specnum_t, std::string > m_monitors
Monitors, map spectrum index to monitor group name.
bool isMultipleTimeRegimeFile(const Nexus::NXEntry &entry) const
Check if is the file is a multiple time regime file.
bool m_have_detector
Is there a detector block.
boost::scoped_ptr< API::ISISRunLogs > m_logCreator
A pointer to the ISISRunLogs creator.
std::shared_ptr< API::Progress > m_progress
Progress reporting object.
void validateMultiPeriodLogs(const Mantid::API::MatrixWorkspace_sptr &)
Check for a set of synthetic logs associated with multi-period log data.
std::string m_instrument_name
The instrument name from Nexus.
std::shared_ptr< HistogramData::HistogramX > m_tof_data
Time channels.
void loadSampleData(DataObjects::Workspace2D_sptr &, const Mantid::Nexus::NXEntry &entry)
Load in details about the sample.
void loadRunDetails(DataObjects::Workspace2D_sptr &local_workspace, Mantid::Nexus::NXEntry &entry)
Load in details about the run.
void loadPeriodData(int64_t period, Mantid::Nexus::NXEntry &entry, DataObjects::Workspace2D_sptr &local_workspace, bool update_spectra2det_mapping=false)
Load a given period into the workspace.
std::vector< specnum_t > m_spec
Spectra numbers.
void runLoadInstrument(DataObjects::Workspace2D_sptr &)
Run LoadInstrument as a ChildAlgorithm.
void loadBlock(Nexus::NXInt &data, int64_t blocksize, int64_t period, int64_t start, int64_t &hist, int64_t &spec_num, DataObjects::Workspace2D_sptr &local_workspace)
Perform a call to nxgetslab, via the NexusClasses wrapped methods for a given block-size.
boost::scoped_ptr< Nexus::File > m_nexusFile
API::SpectrumDetectorMapping m_spec2det_map
spectra Number to detector ID (multi)map
static double dblSqrt(double in)
Personal wrapper for sqrt to allow msvs to compile.
size_t prepareSpectraBlocks(std::map< specnum_t, std::string > &monitors, DataBlockComposite &LoadBlock)
Prepare a vector of SpectraBlock structures to simplify loading.
bool checkOptionalProperties(bool bseparateMonitors, bool bexcludeMonitor)
Check the validity of the optional properties of the algorithm and identify if partial data should be...
std::vector< SpectraBlock > m_spectraBlocks
List of disjoint data blocks to load.
bool m_load_selected_spectra
if true, a spectra list or range of spectra is supplied
void init() override
Overwrites Algorithm method.
static void ProcessLoadMonitorOptions(bool &bincludeMonitors, bool &bseparateMonitors, bool &bexcludeMonitors, const API::Algorithm *pAlgo)
The method to interpret LoadMonitors property options and convert then into boolean values.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
OptionalBool : Tri-state bool.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
The base class for a Nexus class (group).
std::string getString(const std::string &name) const
Returns a string.
bool containsGroup(const std::string &query) const
Returns whether an individual group (or group) is present.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
std::vector< NXClassInfo > & groups() const
Returns a list of all classes (or groups) in this NXClass.
NXClass openNXGroup(const std::string &name) const
Creates and opens an arbitrary (non-standard) class (group).
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
container_T< T > & vecBuffer()
Returns a the internal buffer.
dimsize_t dim0() const
Returns the number of elements along the first dimension.
dimsize_t dim2() const
Returns the number of elements along the third dimension.
void open()
Opens the data set. Does not read in any data. Call load(...) to load the data.
Implements NXdata Nexus class.
NXInt openIntData()
Opens data of int type.
Implements NXentry Nexus class.
NXData openNXData(const std::string &name) const
Opens a NXData.
NexusAddress const & address() const
Returns the absolute address to the object.
std::shared_ptr< File > m_fileID
Nexus file id.
Implements NXroot Nexus class.
NXEntry openEntry(const std::string &name)
Opens an entry – a topmost Nexus class.
bool isEntry(const std::string &entryName, const std::string &groupClass) const noexcept
Checks if a full-address entry exists for a particular groupClass in a Nexus dataset.
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void loadSampleGeometry(API::Sample &sample, const Nexus::NXEntry &entry, const bool hasVMSBlock)
Load geometrical data about the sample from the nexus entry into a workspace.
int64_t findNumberOfSpectra(const Nexus::NXEntry &entry, const bool hasVMSBlock)
find the number of spectra in the nexus file
std::shared_ptr< HistogramData::HistogramX > loadTimeData(const Nexus::NXEntry &entry)
Load the time data from nexus entry.
std::tuple< Nexus::NXInt, Nexus::NXInt > findDetectorIDsAndSpectrumNumber(const Nexus::NXEntry &entry, const bool hasVMSBlock)
find detector ids and spectrum numbers
void loadRunDetails(API::Run &runDetails, const Nexus::NXEntry &entry, const bool hasVMSBlock)
Load data about the run.
void DLLExport populateDataBlockCompositeWithContainer(DataBlockComposite &dataBlockComposite, T &indexContainer, int64_t nArray, size_t numberOfPeriods, size_t numberOfChannels, std::vector< specnum_t > monitorSpectra)
Populates a DataBlockComposite with DataBlocks which are extracted from a indexable collection (array...
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
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
Generate a tableworkspace to store the calibration results.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Output
An output workspace.
Definition Property.h:54