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//----------------------------------------------------------------------
16
17#include "MantidAPI/Axis.h"
20#include "MantidAPI/Sample.h"
30#include "MantidNexus/NexusFile.h"
31
32#include <algorithm>
33#include <cctype>
34#include <climits>
35#include <cmath>
36#include <sstream>
37#include <vector>
38
39namespace {
42 auto dataBlocks = composite.getDataBlocks();
43 auto monitorBlocks = monitors.getDataBlocks();
44 auto matchesMonitorBlock = [&monitorBlocks](Mantid::DataHandling::DataBlock &dataBlock) {
45 return std::find(std::begin(monitorBlocks), std::end(monitorBlocks), dataBlock) != std::end(monitorBlocks);
46 };
47
49 for (auto &dataBlock : dataBlocks) {
50 if (matchesMonitorBlock(dataBlock)) {
51 newComposite.addDataBlock(dataBlock);
52 }
53 }
54
55 return newComposite;
56}
57} // namespace
58
59namespace Mantid::DataHandling {
60
62
63using namespace Kernel;
64using namespace API;
65using namespace Nexus;
66using namespace HistogramData;
67using std::size_t;
68
71 : m_filename(), m_instrument_name(), m_samplename(), m_detBlockInfo(), m_monBlockInfo(), m_loadBlockInfo(),
72 m_have_detector(false), m_hasVMSBlock(false), m_load_selected_spectra(false), m_wsInd2specNum_map(),
73 m_spec2det_map(), m_entrynumber(0), m_tof_data(), m_spec(), m_spec_end(nullptr), m_monitors(), m_logCreator(),
74 m_progress(), m_nexusFile() {}
75
83 if (descriptor.isEntry("/raw_data_1", "NXentry")) {
84 // It also could be an Event Nexus file or a TOFRaw file,
85 // so confidence is set to less than 80.
86 return 75;
87 }
88 return 0;
89}
90
93 const std::vector<std::string> exts{".nxs", ".n*"};
94 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts),
95 "The name of the Nexus file to load");
96 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output));
97
98 auto mustBePositiveSpectrum = std::make_shared<BoundedValidator<specnum_t>>();
99 mustBePositiveSpectrum->setLower(0);
100 declareProperty("SpectrumMin", static_cast<specnum_t>(0), mustBePositiveSpectrum);
101 declareProperty("SpectrumMax", static_cast<specnum_t>(EMPTY_INT()), mustBePositiveSpectrum);
102 declareProperty(std::make_unique<ArrayProperty<specnum_t>>("SpectrumList"));
103 auto mustBePositive = std::make_shared<BoundedValidator<int64_t>>();
104 declareProperty("EntryNumber", static_cast<int64_t>(0), mustBePositive,
105 "0 indicates that every entry is loaded, into a separate "
106 "workspace within a group. "
107 "A positive number identifies one entry to be loaded, into "
108 "one worskspace");
109
110 std::vector<std::string> monitorOptions{"Include", "Exclude", "Separate"};
111 std::map<std::string, std::string> monitorOptionsAliases;
112 monitorOptionsAliases["1"] = "Separate";
113 monitorOptionsAliases["0"] = "Exclude";
114 declareProperty("LoadMonitors", "Include",
115 std::make_shared<Kernel::StringListValidator>(monitorOptions, monitorOptionsAliases),
116 "Option to control the loading of monitors.\n"
117 "Allowed options are Include,Exclude, Separate.\n"
118 "Include:The default is Include option would load monitors with the "
119 "workspace if monitors spectra are within the range of loaded "
120 "detectors.\n"
121 "If the time binning for the monitors is different from the\n"
122 "binning of the detectors this option is equivalent to the Separate "
123 "option\n"
124 "Exclude:Exclude option excludes monitors from the output workspace.\n"
125 "Separate:Separate option loads monitors into a separate workspace "
126 "called: OutputWorkspace_monitors.\n"
127 "Defined aliases:\n"
128 "1: Equivalent to Separate.\n"
129 "0: Equivalent to Exclude.\n");
130}
131
140
141 //**********************************************************************
142 // process load monitor options request
143 bool bincludeMonitors, bseparateMonitors, bexcludeMonitors;
144 LoadRawHelper::ProcessLoadMonitorOptions(bincludeMonitors, bseparateMonitors, bexcludeMonitors, this);
145
146 //**********************************************************************
147 m_filename = getPropertyValue("Filename");
148 // Create the root Nexus class
149 NXRoot root(m_filename);
150
151 // "Open" the same file but with the C++ interface
152 m_nexusFile.reset(new Nexus::File(root.m_fileID));
153
154 // Open the raw data group 'raw_data_1'
155 NXEntry entry = root.openEntry("raw_data_1");
156
157 // Read in the instrument name from the Nexus file
158 m_instrument_name = entry.getString("name");
159
160 // Test if we have a vms block
161 if (entry.containsGroup("isis_vms_compat")) {
162 m_hasVMSBlock = true;
163 }
164
165 // Get number of detectors and spectrum list
166 size_t ndets{0};
167 try {
168 NXClass det_class = entry.openNXGroup("detector_1");
169 NXInt spectrum_index = det_class.openNXInt("spectrum_index");
170 spectrum_index.load();
171 ndets = spectrum_index.dim0();
172 // We assume that this spectrum list increases monotonically
173 m_spec = spectrum_index.vecBuffer();
174 m_spec_end = m_spec.data() + ndets;
175 m_have_detector = true;
176 } catch (std::runtime_error &) {
177 ndets = 0;
178 }
179
180 // Load detector and spectra ids, and number of monitors + detectors?
183
184 // Pull out the monitor blocks, if any exist
185 size_t nmons{0};
186
187 for (auto it = entry.groups().cbegin(); it != entry.groups().cend(); ++it) {
188 if (it->nxclass == "NXmonitor") // Count monitors
189 {
190 NXInt index = entry.openNXInt(std::string(it->nxname) + "/spectrum_index");
191 index.load();
192 specnum_t ind = static_cast<specnum_t>(*index());
193 // Spectrum index of 0 means no spectrum associated with that monitor,
194 // so only count those with index > 0
195 if (ind > 0) {
196 m_monitors[ind] = it->nxname;
197 ++nmons;
198 }
199 }
200 }
201
202 if (ndets == 0 && nmons == 0) {
203 if (bexcludeMonitors) {
204 g_log.warning() << "Nothing to do. No detectors found and no monitor "
205 "loading requested";
206 return;
207 } else {
208 g_log.error() << "Invalid NeXus structure, cannot find detector or monitor blocks.";
209 throw std::runtime_error("Inconsistent NeXus file structure.");
210 }
211 }
212
213 // Determine the data block for the detectors and monitors
214 bseparateMonitors =
215 findSpectraDetRangeInFile(entry, m_spec, ndets, nsp1, m_monitors, bexcludeMonitors, bseparateMonitors);
216
217 size_t x_length = m_loadBlockInfo.getNumberOfChannels() + 1;
218
219 // Check input is consistent with the file, throwing if not
220 bseparateMonitors = checkOptionalProperties(bseparateMonitors, bexcludeMonitors);
221 // Fill up m_spectraBlocks
222 size_t total_specs = prepareSpectraBlocks(m_monitors, m_loadBlockInfo);
223
224 m_progress = std::make_shared<API::Progress>(this, 0.0, 1.0, total_specs * m_detBlockInfo.getNumberOfPeriods());
225
226 DataObjects::Workspace2D_sptr local_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
227 WorkspaceFactory::Instance().create("Workspace2D", total_specs, x_length, m_loadBlockInfo.getNumberOfChannels()));
228 // Set the units on the workspace to TOF & Counts
229 local_workspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
230 local_workspace->setYUnit("Counts");
231
232 // Load instrument and other data once then copy it later
233 m_progress->report("Loading instrument and run details");
234
235 // load run details
236 loadRunDetails(local_workspace, entry);
237
238 // Test if IDF exists in Nexus otherwise load default instrument
239 bool foundInstrument = LoadEventNexus::runLoadIDFFromNexus(m_filename, local_workspace, "raw_data_1", this);
241 m_spec2det_map = SpectrumDetectorMapping(spec(), udet(), udet.dim0());
242 else if (bseparateMonitors) {
243 m_spec2det_map = SpectrumDetectorMapping(spec(), udet(), udet.dim0());
244 local_workspace->updateSpectraUsing(m_spec2det_map);
245 } else {
246 local_workspace->updateSpectraUsing(SpectrumDetectorMapping(spec(), udet(), udet.dim0()));
247 }
248
249 if (!foundInstrument) {
250 runLoadInstrument(local_workspace);
251 }
252
253 // Load logs and sample information
254 m_nexusFile->openAddress(entry.address());
255 local_workspace->loadSampleAndLogInfoNexus(m_nexusFile.get());
256
257 // Load logs and sample information further information... See maintenance
258 // ticket #8697
259 loadSampleData(local_workspace, entry);
260 m_progress->report("Loading logs");
261 loadLogs(local_workspace);
262
263 // Load first period outside loop
264 m_progress->report("Loading data");
265 // Get X Data
266 if (ndets > 0) {
268 }
269
270 int64_t firstentry = (m_entrynumber > 0) ? m_entrynumber : 1;
271 loadPeriodData(firstentry, entry, local_workspace, m_load_selected_spectra);
272
273 // Clone the workspace at this point to provide a base object for future
274 // workspace generation.
275 DataObjects::Workspace2D_sptr period_free_workspace =
276 std::dynamic_pointer_cast<DataObjects::Workspace2D>(WorkspaceFactory::Instance().create(local_workspace));
277
278 createPeriodLogs(firstentry, local_workspace);
279
280 WorkspaceGroup_sptr wksp_group(new WorkspaceGroup);
282 wksp_group->setTitle(local_workspace->getTitle());
283
284 // This forms the name of the group
285 const std::string base_name = getPropertyValue("OutputWorkspace") + "_";
286 const std::string prop_name = "OutputWorkspace_";
287
288 for (std::size_t p = 1; p <= m_loadBlockInfo.getNumberOfPeriods(); ++p) {
289 std::ostringstream os;
290 os << p;
291 m_progress->report("Loading period " + os.str());
292 if (p > 1) {
293 local_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
294 WorkspaceFactory::Instance().create(period_free_workspace));
295 loadPeriodData(p, entry, local_workspace, m_load_selected_spectra);
296 createPeriodLogs(p, local_workspace);
297 // Check consistency of logs data for multi-period workspaces and raise
298 // warnings where necessary.
299 validateMultiPeriodLogs(local_workspace);
300 }
301 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>(prop_name + os.str(), base_name + os.str(),
303 wksp_group->addWorkspace(local_workspace);
304 setProperty(prop_name + os.str(), std::static_pointer_cast<Workspace>(local_workspace));
305 }
306 // The group is the root property value
307 setProperty("OutputWorkspace", std::dynamic_pointer_cast<Workspace>(wksp_group));
308 } else {
309 setProperty("OutputWorkspace", std::dynamic_pointer_cast<Workspace>(local_workspace));
310 }
311
312 //***************************************************************************************************
313 // Workspace or group of workspaces without monitors is loaded. Now we are
314 // loading monitors separately.
315 if (bseparateMonitors) {
316 std::string wsName = getPropertyValue("OutputWorkspace");
318 x_length = m_monBlockInfo.getNumberOfChannels() + 1;
319 // reset the size of the period free workspace to the monitor size
320 period_free_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(WorkspaceFactory::Instance().create(
321 period_free_workspace, m_monBlockInfo.getNumberOfSpectra(), x_length, m_monBlockInfo.getNumberOfChannels()));
322 auto monitor_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
323 WorkspaceFactory::Instance().create(period_free_workspace));
324
325 m_spectraBlocks.clear();
326 m_wsInd2specNum_map.clear();
327 // at the moment here we clear this map to enable possibility to load
328 // monitors from the spectra block (wiring table bug).
329 // if monitor's spectra present in the detectors block due to this bug
330 // should be read from monitors, this map should be dealt with properly.
331 buildSpectraInd2SpectraNumMap(true /*hasRange*/, false /*hasSpectraList*/, m_monBlockInfo);
332
333 // lo
335
336 firstentry = (m_entrynumber > 0) ? m_entrynumber : 1;
337 loadPeriodData(firstentry, entry, monitor_workspace, true);
338 local_workspace->setMonitorWorkspace(monitor_workspace);
339
340 ISISRunLogs monLogCreator(monitor_workspace->run());
341 monLogCreator.addPeriodLogs(1, monitor_workspace->mutableRun());
342
343 const std::string monitorPropBase = "MonitorWorkspace";
344 const std::string monitorWsNameBase = wsName + "_monitors";
346 WorkspaceGroup_sptr monitor_group(new WorkspaceGroup);
347 monitor_group->setTitle(monitor_workspace->getTitle());
348
349 for (std::size_t p = 1; p <= m_detBlockInfo.getNumberOfPeriods(); ++p) {
350 std::ostringstream os;
351 os << "_" << p;
352 m_progress->report("Loading period " + os.str());
353 if (p > 1) {
354 monitor_workspace = std::dynamic_pointer_cast<DataObjects::Workspace2D>(
355 WorkspaceFactory::Instance().create(period_free_workspace));
356 loadPeriodData(p, entry, monitor_workspace, m_load_selected_spectra);
357 monLogCreator.addPeriodLogs(static_cast<int>(p), monitor_workspace->mutableRun());
358 // Check consistency of logs data for multi-period workspaces and
359 // raise
360 // warnings where necessary.
361 validateMultiPeriodLogs(monitor_workspace);
362 auto data_ws = std::static_pointer_cast<API::MatrixWorkspace>(wksp_group->getItem(p - 1));
363 data_ws->setMonitorWorkspace(monitor_workspace);
364 }
366 monitorPropBase + os.str(), monitorWsNameBase + os.str(), Direction::Output));
367 monitor_group->addWorkspace(monitor_workspace);
368 setProperty(monitorPropBase + os.str(), std::static_pointer_cast<Workspace>(monitor_workspace));
369 }
370 // The group is the root property value
372 std::make_unique<WorkspaceProperty<Workspace>>(monitorPropBase, monitorWsNameBase, Direction::Output));
373 setProperty(monitorPropBase, std::dynamic_pointer_cast<Workspace>(monitor_group));
374
375 } else {
377 std::make_unique<WorkspaceProperty<Workspace>>(monitorPropBase, monitorWsNameBase, Direction::Output));
378 setProperty(monitorPropBase, std::static_pointer_cast<Workspace>(monitor_workspace));
379 }
380 } else {
381 g_log.information() << " no monitors to load for workspace: " << wsName << '\n';
382 }
383 }
384
385 // Map spectra whose file detector IDs the instrument does not define onto the detector of the same ID; a no-op
386 // unless the instrument definition enables it. Left until here so every period and monitor workspace are populated.
388
389 // Clear off the member variable containers
390 m_tof_data.reset();
391 m_spec.clear();
392 m_monitors.clear();
393 m_wsInd2specNum_map.clear();
394 m_nexusFile->close();
395}
401 const Run &run = ws->run();
402 if (!run.hasProperty("current_period")) {
403 g_log.warning("Workspace has no current_period log.");
404 }
405 if (!run.hasProperty("nperiods")) {
406 g_log.warning("Workspace has no nperiods log");
407 }
408 if (!run.hasProperty("proton_charge_by_period")) {
409 g_log.warning("Workspace has not proton_charge_by_period log");
410 }
411}
412
420bool LoadISISNexus2::checkOptionalProperties(bool bseparateMonitors, bool bexcludeMonitor) {
421 // optional properties specify that only some spectra have to be loaded
422 bool range_supplied(false);
423
424 // Get the spectrum selection which were specfied by the user
425 specnum_t spec_min = getProperty("SpectrumMin");
426 specnum_t spec_max = getProperty("SpectrumMax");
427
428 // If spearate monitors or excluded monitors is selected then we
429 // need to build up a wsIndex to spectrum number map as well,
430 // since we cannot rely on contiguous blocks of detectors
431 if (bexcludeMonitor || bseparateMonitors) {
433 }
434
435 if (spec_min == 0)
437 else {
438 range_supplied = true;
440 }
441
442 if (spec_max == EMPTY_INT())
444 else {
445 range_supplied = true;
447 }
448
449 // Sanity check for min/max
450 if (spec_min > spec_max) {
451 throw std::invalid_argument("Inconsistent range properties. SpectrumMin is "
452 "larger than SpectrumMax.");
453 }
454
455 if (spec_max > m_loadBlockInfo.getMaxSpectrumID()) {
456 std::string err = "Inconsistent range property. SpectrumMax is larger than number of "
457 "spectra: " +
459 throw std::invalid_argument(err);
460 }
461
462 // Check the entry number
463 m_entrynumber = getProperty("EntryNumber");
464 if (static_cast<size_t>(m_entrynumber) > m_loadBlockInfo.getNumberOfPeriods()) {
465 std::string err = "Invalid entry number entered. File contains " +
467 throw std::invalid_argument(err);
468 }
469
471 m_entrynumber = 1;
472 }
473
474 // Did the user provide a spectrum list
475 std::vector<specnum_t> spec_list = getProperty("SpectrumList");
476 auto hasSpecList = false;
477
478 if (!spec_list.empty()) {
480
481 // Sort the list so that we can check it's range
482 std::sort(spec_list.begin(), spec_list.end());
483
484 // Check if the spectra list entries are outside of the bounds
485 // If we load the monitors separately, then we need to make sure that we
486 // take them into account
487 bool isSpectraListTooLarge;
488 bool isSpectraListTooSmall;
489 auto maxLoadBlock = m_loadBlockInfo.getMaxSpectrumID();
490 auto minLoadBlock = m_loadBlockInfo.getMinSpectrumID();
491 if (bseparateMonitors) {
492 auto maxMonBlock = m_monBlockInfo.getMaxSpectrumID();
493 auto minMonBlock = m_monBlockInfo.getMinSpectrumID();
494 isSpectraListTooLarge = spec_list.back() > std::max(maxMonBlock, maxLoadBlock);
495 isSpectraListTooSmall = spec_list.front() < std::min(minMonBlock, minLoadBlock);
496
497 } else {
498 isSpectraListTooLarge = spec_list.back() > maxLoadBlock;
499 isSpectraListTooSmall = spec_list.front() < minLoadBlock;
500 }
501
502 if (isSpectraListTooLarge) {
503 std::string err = "The specified spectrum list contains a spectrum number which is "
504 "larger "
505 "than the largest loadable spectrum number for your selection of "
506 "excluded/included/separate monitors. The largest loadable "
507 "spectrum number is " +
509 throw std::invalid_argument(err);
510 }
511 if (isSpectraListTooSmall) {
512 std::string err = "The specified spectrum list contains a spectrum number which is "
513 "smaller "
514 "than the smallest loadable spectrum number for your selection of "
515 "excluded/included/separate monitors. The smallest loadable "
516 "spectrum number is " +
518 throw std::invalid_argument(err);
519 }
520
521 // The users can provide a spectrum list and and a spectrum range. Handle
522 // this here.
523 if (range_supplied) {
524 // First remove all entries which are inside of the min and max spectrum,
525 // to avoid duplicates
526 auto isInRange = [&spec_min, &spec_max](specnum_t x) { return (spec_min <= x) && (x <= spec_max); };
527
528 spec_list.erase(remove_if(spec_list.begin(), spec_list.end(), isInRange), spec_list.end());
529
530 // The spec_min - spec_max range needs to be added to the spec list
531 for (auto i = spec_min; i < spec_max + 1; ++i) {
532 spec_list.emplace_back(i);
533 }
534 std::sort(spec_list.begin(), spec_list.end());
535 }
536
537 auto monitorSpectra = m_monBlockInfo.getAllSpectrumNumbers();
538 // Create DataBlocks from the spectrum list
539 DataBlockComposite composite;
540 populateDataBlockCompositeWithContainer(composite, spec_list, spec_list.size(),
542 monitorSpectra);
543
544 // If the monitors are to be loaded separately, then we have
545 // to remove them at this point, but we also have to check if the
546 if (bexcludeMonitor || bseparateMonitors) {
547 auto newMonitors = getMonitorsFromComposite(composite, m_monBlockInfo);
548 composite.removeSpectra(m_monBlockInfo);
549
550 // This is important. If there are no monitors which were specifically
551 // selected,
552 // then we load the full monitor range, else respect the selection.
553 if (!newMonitors.isEmpty()) {
554 m_monBlockInfo = newMonitors;
555 }
556
557 // Handle case where the composite is empty since it only contained
558 // monitors, but we want to load the monitors sepearately. In this case we
559 // should set the loadBlock to the selected monitors.
560 if (bseparateMonitors && composite.isEmpty()) {
561 composite = m_monBlockInfo;
562 bseparateMonitors = false;
563 }
564 }
565
566 m_loadBlockInfo = composite;
567
568 hasSpecList = true;
569 } else {
570 // At this point we don't have a spectrum list but there might have been a
571 // spectrum range which we need to take into account, by truncating
572 // the current range. If we load the monitors separately, we need to
573 // truncate them as well (provided they are affected)
574 if (range_supplied) {
575 m_loadBlockInfo.truncate(spec_min, spec_max);
576
577 auto new_monitors = m_monBlockInfo;
578 new_monitors.truncate(spec_min, spec_max);
579 m_monBlockInfo = new_monitors;
580 }
581 }
582
584 buildSpectraInd2SpectraNumMap(range_supplied, hasSpecList, m_loadBlockInfo);
585 }
586
587 // Check that the load blocks contain anything at all.
588 if (m_loadBlockInfo.isEmpty()) {
589 throw std::invalid_argument("Your spectrum number selection was not valid. "
590 "Make sure that you select spectrum numbers "
591 "and ranges which are compatible with your "
592 "selection of excluded/included/separate monitors. ");
593 }
594
595 return bseparateMonitors;
596}
597
608void LoadISISNexus2::buildSpectraInd2SpectraNumMap(bool range_supplied, bool hasSpectraList,
609 const DataBlockComposite &dataBlockComposite) {
610
611 if (range_supplied || hasSpectraList || true) {
612 auto generator = dataBlockComposite.getGenerator();
613 int64_t hist = 0;
614 for (; !generator->isDone(); generator->next()) {
615 auto spec_num = static_cast<specnum_t>(generator->getValue());
616 m_wsInd2specNum_map.emplace(hist, spec_num);
617 ++hist;
618 }
619 }
620}
621
628size_t LoadISISNexus2::prepareSpectraBlocks(std::map<specnum_t, std::string> &monitors, DataBlockComposite &LoadBlock) {
629 std::vector<specnum_t> includedMonitors;
630 // Setup the SpectraBlocks based on the DataBlocks
631 auto dataBlocks = LoadBlock.getDataBlocks();
632 auto isMonitor = [&monitors](specnum_t spectrumNumber) { return monitors.find(spectrumNumber) != monitors.end(); };
633 for (const auto &dataBlock : dataBlocks) {
634 auto min = dataBlock.getMinSpectrumID();
635 if (isMonitor(min)) {
636 m_spectraBlocks.emplace_back(SpectraBlock(min, min, true, monitors.find(min)->second));
637 includedMonitors.emplace_back(min);
638 } else {
639 auto max = dataBlock.getMaxSpectrumID();
640 m_spectraBlocks.emplace_back(min, max, false, "");
641 }
642 }
643
644 // sort and check for overlapping
645 if (m_spectraBlocks.size() > 1) {
646 std::sort(m_spectraBlocks.begin(), m_spectraBlocks.end(),
647 [](const LoadISISNexus2::SpectraBlock &block1, const LoadISISNexus2::SpectraBlock &block2) {
648 return block1.last < block2.first;
649 });
651 }
652
653 // Remove monitors that have been used.
654 auto allMonitorsIncluded = monitors.size() == includedMonitors.size();
655 if (!includedMonitors.empty() && !allMonitorsIncluded) {
656 for (auto it = monitors.begin(); it != monitors.end();) {
657 if (std::find(includedMonitors.begin(), includedMonitors.end(), it->first) != includedMonitors.end()) {
658 auto it1 = it;
659 ++it;
660 monitors.erase(it1);
661 } else {
662 ++it;
663 }
664 }
665 }
666
667 // Count the number of spectra.
668 const auto nSpec = std::accumulate(
669 m_spectraBlocks.cbegin(), m_spectraBlocks.cend(), static_cast<size_t>(0),
670 [](size_t sum, const auto &spectraBlock) { return sum + spectraBlock.last - spectraBlock.first + 1; });
671 return nSpec;
672}
673
682 for (size_t i = 1; i < m_spectraBlocks.size(); ++i) {
683 const auto &block1 = m_spectraBlocks[i - 1];
684 const auto &block2 = m_spectraBlocks[i];
685 if (block1.first > block1.last && block2.first > block2.last)
686 throw std::runtime_error("LoadISISNexus2: inconsistent spectra ranges");
687 if (block1.last >= block2.first) {
688 throw std::runtime_error("LoadISISNexus2: the range of SpectraBlocks must not overlap");
689 }
690 }
691}
692
702void LoadISISNexus2::loadPeriodData(int64_t period, NXEntry &entry, DataObjects::Workspace2D_sptr &local_workspace,
703 bool update_spectra2det_mapping) {
704 int64_t hist_index = 0;
705 int64_t period_index(period - 1);
706
707 for (const auto &spectraBlock : m_spectraBlocks) {
708 if (spectraBlock.isMonitor) {
709 NXData monitor = entry.openNXData(spectraBlock.monName);
710 NXInt mondata = monitor.openIntData();
711 m_progress->report("Loading monitor");
712 mondata.load(1, static_cast<int>(period - 1)); // TODO this is just wrong
713 NXFloat timeBins = monitor.openNXFloat("time_of_flight");
714 timeBins.load();
715 local_workspace->setHistogram(hist_index, BinEdges(timeBins(), timeBins() + timeBins.dim0()),
716 Counts(mondata(), mondata() + m_monBlockInfo.getNumberOfChannels()));
717
718 if (update_spectra2det_mapping) {
719 auto &spec = local_workspace->getSpectrum(hist_index);
720 specnum_t specNum = m_wsInd2specNum_map.at(hist_index);
721 spec.setDetectorIDs(m_spec2det_map.getDetectorIDsForSpectrumNo(specNum));
722 spec.setSpectrumNo(specNum);
723 }
724 hist_index++;
725 } else if (m_have_detector) {
726 NXData nxdata = entry.openNXData("detector_1");
727 NXInt data = nxdata.openIntData();
728 data.open();
729 // Start with the list members that are lower than the required spectrum
730 const int *const spec_begin = m_spec.data();
731 // When reading in blocks we need to be careful that the range is exactly
732 // divisible by the block-size
733 // and if not have an extra read of the left overs
734 const int64_t blocksize = 8;
735 const int64_t rangesize = spectraBlock.last - spectraBlock.first + 1;
736 const int64_t fullblocks = rangesize / blocksize;
737 int64_t spectra_no = spectraBlock.first;
738
739 // For this to work correctly, we assume that the spectrum list increases
740 // monotonically
741 int64_t filestart = std::lower_bound(spec_begin, m_spec_end, spectra_no) - spec_begin;
742 if (fullblocks > 0) {
743 for (int64_t i = 0; i < fullblocks; ++i) {
744 loadBlock(data, blocksize, period_index, filestart, hist_index, spectra_no, local_workspace);
745 filestart += blocksize;
746 }
747 }
748 int64_t finalblock = rangesize - (fullblocks * blocksize);
749 if (finalblock > 0) {
750 loadBlock(data, finalblock, period_index, filestart, hist_index, spectra_no, local_workspace);
751 }
752 }
753 }
754
755 try {
756 const std::string title = entry.getString("title");
757 local_workspace->setTitle(title);
758 // write the title into the log file (run object)
759 local_workspace->mutableRun().addProperty("run_title", title, true);
760 } catch (std::runtime_error &) {
761 g_log.debug() << "No title was found in the input file, " << getPropertyValue("Filename") << '\n';
762 }
763
764 std::string notes = "";
765 try {
766 notes = entry.getString("notes");
767 } catch (std::runtime_error &) {
768 // if no notes, add empty string
769 }
770 local_workspace->setComment(notes);
771}
772
779 m_logCreator->addPeriodLogs(static_cast<int>(period), local_workspace->mutableRun());
780}
781
793void LoadISISNexus2::loadBlock(NXInt &data, int64_t blocksize, int64_t period, int64_t start, int64_t &hist,
794 int64_t &spec_num, DataObjects::Workspace2D_sptr &local_workspace) {
795 data.load(blocksize, period, start); // TODO this is just wrong
796 int *data_start = data();
797 int *data_end = data_start + m_loadBlockInfo.getNumberOfChannels();
798 int64_t final(hist + blocksize);
799 while (hist < final) {
800 m_progress->report("Loading data");
801 local_workspace->setHistogram(hist, BinEdges(m_tof_data), Counts(data_start, data_end));
802 data_start += m_detBlockInfo.getNumberOfChannels();
805 auto &spec = local_workspace->getSpectrum(hist);
806 specnum_t specNum = m_wsInd2specNum_map.at(hist);
807 // set detectors corresponding to spectra Number
808 spec.setDetectorIDs(m_spec2det_map.getDetectorIDsForSpectrumNo(specNum));
809 // set correct spectra Number
810 spec.setSpectrumNo(specNum);
811 }
812
813 ++hist;
814 ++spec_num;
815 }
816}
817
820
821 auto loadInst = createChildAlgorithm("LoadInstrument");
822
823 // Now execute the Child Algorithm. Catch and log any error, but don't stop.
824 bool executionSuccessful(true);
825 try {
826 loadInst->setPropertyValue("InstrumentName", m_instrument_name);
827 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
828 loadInst->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(false));
829 loadInst->execute();
830 } catch (std::invalid_argument &) {
831 g_log.information("Invalid argument to LoadInstrument Child Algorithm");
832 executionSuccessful = false;
833 } catch (std::runtime_error &) {
834 g_log.information("Unable to successfully run LoadInstrument Child Algorithm");
835 executionSuccessful = false;
836 }
837 if (executionSuccessful) {
838 // If requested update the instrument to positions in the data file
839 const auto &pmap = localWorkspace->constInstrumentParameters();
840 if (pmap.contains(localWorkspace->getInstrument()->getComponentID(), "det-pos-source")) {
841 std::shared_ptr<Geometry::Parameter> updateDets =
842 pmap.get(localWorkspace->getInstrument()->getComponentID(), "det-pos-source");
843 std::string value = updateDets->value<std::string>();
844 if (value.substr(0, 8) == "datafile") {
845 auto updateInst = createChildAlgorithm("UpdateInstrumentFromFile");
846 updateInst->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
847 updateInst->setPropertyValue("Filename", m_filename);
848 if (value == "datafile-ignore-phi") {
849 updateInst->setProperty("IgnorePhi", true);
850 g_log.information("Detector positions in IDF updated with positions "
851 "in the data file except for the phi values");
852 } else {
853 g_log.information("Detector positions in IDF updated with positions "
854 "in the data file");
855 }
856 // We want this to throw if it fails to warn the user that the
857 // information is not correct.
858 updateInst->execute();
859 }
860 }
861 }
862}
863
870
871 API::Run &runDetails = local_workspace->mutableRun();
872
873 // Data details on run not the workspace
874 runDetails.addProperty("nspectra", static_cast<int>(m_loadBlockInfo.getNumberOfSpectra()));
875 runDetails.addProperty("nchannels", static_cast<int>(m_loadBlockInfo.getNumberOfChannels()));
876 runDetails.addProperty("nperiods", static_cast<int>(m_loadBlockInfo.getNumberOfPeriods()));
877
879}
880
887
888 // load sample geometry - Id and dimensions
889 LoadISISNexusHelper::loadSampleGeometry(local_workspace->mutableSample(), entry, m_hasVMSBlock);
890 g_log.debug() << "Sample geometry - ID: " << local_workspace->mutableSample().getGeometryFlag()
891 << ", thickness: " << local_workspace->mutableSample().getThickness()
892 << ", height: " << local_workspace->mutableSample().getHeight()
893 << ", width: " << local_workspace->mutableSample().getWidth() << "\n";
894}
895
902 auto alg = createChildAlgorithm("LoadNexusLogs", 0.0, 0.5);
903 alg->setPropertyValue("Filename", this->getProperty("Filename"));
904 alg->setProperty<MatrixWorkspace_sptr>("Workspace", ws);
905 try {
906 alg->executeAsChildAlg();
907 } catch (std::runtime_error &) {
908 g_log.warning() << "Unable to load run logs. There will be no log "
909 << "data associated with this workspace\n";
910 return;
911 }
912
913 // Populate the instrument parameters.
914 ws->populateInstrumentParameters();
915
916 // Make log creator object and add the run status log
917 m_logCreator.reset(new API::ISISRunLogs(ws->run()));
918 m_logCreator->addStatusLog(ws->mutableRun());
919}
920
921double LoadISISNexus2::dblSqrt(double in) { return sqrt(in); }
947bool LoadISISNexus2::findSpectraDetRangeInFile(const NXEntry &entry, std::vector<specnum_t> &spectrum_index,
948 int64_t ndets, int64_t n_vms_compat_spectra,
949 const std::map<specnum_t, std::string> &monitors, bool excludeMonitors,
950 bool separateMonitors) {
951 size_t nmons = monitors.size();
952
953 if (nmons > 0) {
954 NXInt chans = entry.openNXInt(m_monitors.begin()->second + "/data");
955
956 // Iterate over each monitor and create a data block for each monitor
957 for (const auto &monitor : monitors) {
958 auto monID = monitor.first;
959 auto monTemp = DataBlock(chans);
960 monTemp.setMinSpectrumID(monID);
961 monTemp.setMaxSpectrumID(monID);
963 }
964
965 // at this stage we assume that the only going to load monitors
967 }
968
969 // Check if the there are only monitors in the workspace, in which
970 // case the monitors are loaded as the main workspace, ie not as
971 // a separate workspace.
972 if (ndets == 0) {
973 separateMonitors = false;
974 return separateMonitors;
975 }
976
977 // There are detectors present. The spectrum_index array contains
978 // all available spectra of detectors, but these indices might
979 // not be contiguous.
980 NXData nxData = entry.openNXData("detector_1");
981 NXInt data = nxData.openIntData();
982
983 auto monitorSpectra = m_monBlockInfo.getAllSpectrumNumbers();
985 static_cast<int>(data.dim0()) /*Number of Periods*/,
986 data.dim2() /*Number of channels*/, std::move(monitorSpectra));
987
988 // We should handle legacy files which include the spectrum number of the
989 // monitors
990 // in the detector group ("raw_data_1/detector_1/spectrum_index")
991 // Simple try to remove the monitors. If they are not included nothing should
992 // happen
994
996
997 // Check what is actually going or can be loaded
998 bool removeMonitors = excludeMonitors || separateMonitors;
999
1000 // If the monitors are to be pulled into the same workspace as the detector
1001 // information,
1002 // then the number of periods and the number of channels has to conincide
1005 if (!removeMonitors) {
1006 g_log.warning() << " Performing separate loading as can not load spectra "
1007 "and monitors in the single workspace:\n";
1008 g_log.warning() << " Monitors data contain :" << m_monBlockInfo.getNumberOfChannels()
1009 << " time channels and: " << m_monBlockInfo.getNumberOfPeriods() << " period(s)\n";
1010 g_log.warning() << " Spectra data contain :" << m_detBlockInfo.getNumberOfChannels()
1011 << " time channels and: " << m_detBlockInfo.getNumberOfPeriods() << " period(s)\n";
1012 }
1013
1014 // Force the monitors to be removed and separate if the periods and channels
1015 // don't conincide
1016 // between monitors and detectors.
1017 separateMonitors = true;
1018 removeMonitors = true;
1019 }
1020
1021 int64_t spectraID_min = std::min(m_monBlockInfo.getMinSpectrumID(), m_detBlockInfo.getMinSpectrumID());
1022 int64_t spectraID_max = std::max(m_monBlockInfo.getMaxSpectrumID(), m_detBlockInfo.getMaxSpectrumID());
1024
1025 // In case we want to load everything into a one workspace, we should combine
1026 // the
1027 // the data blocks of the monitor and the detector
1028 if (!removeMonitors) {
1030 }
1031
1032 // If the monitors are to be loaded separately, then we set the loadblocks to
1033 // the detblocks,
1034 // since we want to deal with the detectors (the main workspace) first.
1035 if (separateMonitors)
1037
1038 // Perform a sanity check of the spectrum numbers
1039 if ((totNumOfSpectra != static_cast<size_t>(n_vms_compat_spectra)) ||
1040 (spectraID_max - spectraID_min + 1 != static_cast<int64_t>(n_vms_compat_spectra))) {
1041 // At this point we normally throw since there is a mismatch between the
1042 // number
1043 // spectra of the detectors+monitors and the entry in NSP1, but in the
1044 // case of multiple time regimes this comparison is not any longer valid.
1045 // Hence we only throw if the file does not correspond to a multiple time
1046 // regime file.
1047 if (!isMultipleTimeRegimeFile(entry)) {
1048 throw std::runtime_error("LoadISISNexus: There seems to be an "
1049 "inconsistency in the spectrum numbers.");
1050 }
1051 }
1052
1053 return separateMonitors;
1054}
1055
1064 auto hasMultipleTimeRegimes(false);
1065 try {
1066 NXClass instrument = entry.openNXGroup("instrument");
1067 NXClass dae = instrument.openNXGroup("dae");
1068 hasMultipleTimeRegimes = dae.containsGroup("time_channels_3");
1069 } catch (...) {
1070 }
1071 return hasMultipleTimeRegimes;
1072}
1073
1074} // 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:423
@ 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:90
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:36
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)
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.
int confidence(Nexus::NexusDescriptorLazy &descriptor) const override
Returns a confidence value that this algorithm can load a file.
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(std::string const &entryName, std::string const &groupClass) const
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...
MANTID_DATAHANDLING_DLL void correctLoadedWorkspaces(const API::Algorithm &loader, Kernel::Logger &log)
Applies correctSpectraMapping to whichever workspaces a loader declared: its output,...
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