Mantid
Loading...
Searching...
No Matches
Run.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#include "MantidAPI/Run.h"
11#include "MantidKernel/Matrix.h"
16#include "MantidNexus/NexusFile.h"
17
18#include <boost/lexical_cast.hpp>
19#include <memory>
20
21#include <algorithm>
22#include <functional>
23#include <numeric>
24
25namespace Mantid::API {
26
27using namespace Kernel;
28using Mantid::Types::Core::DateAndTime;
29
30namespace {
32Kernel::Logger g_log("Run");
33
34auto addPeriodSeries = [](Property *left, const Property *right) {
35 auto *leftAP = dynamic_cast<PropertyWithValue<std::vector<double>> *>(left);
36 const auto *rightAP = dynamic_cast<const PropertyWithValue<std::vector<double>> *>(right);
37 if (!leftAP || !rightAP) {
38 throw std::runtime_error("Expected ArrayProperty<double> for both inputs.");
39 }
40 const auto &leftVec = leftAP->operator()();
41 const auto &rightVec = rightAP->operator()();
42 if (leftVec.size() != rightVec.size()) {
44 "Summing runs with differing number of periods. Proton charge logs will retain values from LHS operand.");
45 return;
46 }
47 std::vector<double> resVec;
48 resVec.reserve(leftVec.size());
49 for (size_t i = 0; i < leftVec.size(); i++) {
50 resVec.push_back(leftVec[i] + rightVec[i]);
51 }
52 *leftAP = resVec;
53};
54
56struct addableProperty {
57public:
58 using func = std::function<void(Property *, const Property *)>;
59 addableProperty(const std::string &name, func opFunc_in = nullptr) : name(name), opFunc(std::move(opFunc_in)) {};
60 std::string name;
61 func opFunc;
62};
63
65const std::vector<addableProperty> ADDABLE{addableProperty("tot_prtn_chrg"),
66 addableProperty("rawfrm"),
67 addableProperty("goodfrm"),
68 addableProperty("dur"),
69 addableProperty("gd_prtn_chrg"),
70 addableProperty("uA.hour"),
71 addableProperty("monitor0_counts"),
72 addableProperty("monitor1_counts"),
73 addableProperty("monitor2_counts"),
74 addableProperty("monitor3_counts"),
75 addableProperty("monitor4_counts"),
76 addableProperty("monitor5_counts"),
77 addableProperty("proton_charge_by_period", addPeriodSeries)};
78
80const char *GONIOMETER_LOG_NAME = "goniometer";
81const char *GONIOMETERS_LOG_NAME = "goniometers";
83const char *HISTO_BINS_LOG_NAME = "processed_histogram_bins";
84const char *PEAK_RADIUS_GROUP = "peak_radius";
85const char *INNER_BKG_RADIUS_GROUP = "inner_bkg_radius";
86const char *OUTER_BKG_RADIUS_GROUP = "outer_bkg_radius";
87} // namespace
88
90 m_goniometers.clear();
91 m_goniometers.push_back(std::make_unique<Geometry::Goniometer>());
92}
93
94Run::Run(const Run &other) : LogManager(other), m_histoBins(other.m_histoBins) { this->copyGoniometers(other); }
95
96// Defined as default in source for forward declaration with std::unique_ptr.
97Run::~Run() = default;
98
99Run &Run::operator=(const Run &other) {
100 if (this == &other)
101 return *this;
103 copyGoniometers(other);
104 m_histoBins = other.m_histoBins;
105 return *this;
106}
107
108bool Run::operator==(const Run &other) {
109 if (m_goniometers.size() != other.m_goniometers.size())
110 return false;
111 for (size_t i = 0; i < m_goniometers.size(); i++) {
112 if (*m_goniometers[i] != *other.m_goniometers[i])
113 return false;
114 }
115 return LogManager::operator==(other) && this->m_histoBins == other.m_histoBins;
116}
117
118bool Run::operator!=(const Run &other) { return !this->operator==(other); }
119
120std::shared_ptr<Run> Run::clone() {
121 auto clone = std::make_shared<Run>();
122 for (auto property : this->m_manager->getProperties()) {
123 clone->addProperty(property->clone());
124 }
125 clone->copyGoniometers(const_cast<Run &>(*this));
126 clone->m_histoBins = this->m_histoBins;
127 return clone;
128}
129
130//-----------------------------------------------------------------------------------------------
141void Run::filterByTime(const Types::Core::DateAndTime start, const Types::Core::DateAndTime stop) {
142 LogManager::filterByTime(start, stop);
143 // Re-integrate proton charge
144 this->integrateProtonCharge();
145}
146
147namespace {
148void findAndConcatenateTimeStrProp(const Run *runObjLHS, const Run *runObjRHS, const std::string &firstSuggestion,
149 const std::string &secondSuggestion, std::string &propName, std::string &propValue) {
150 // get the name/value from the right-hand-side
151 // this should get overwritten below by the left
152 std::string rhsValue;
153 if (runObjRHS->hasProperty(firstSuggestion)) {
154 propName = firstSuggestion;
155 rhsValue = runObjRHS->getProperty(firstSuggestion)->value();
156 } else if (runObjRHS->hasProperty(secondSuggestion)) {
157 propName = secondSuggestion;
158 rhsValue = runObjRHS->getProperty(secondSuggestion)->value();
159 }
160
161 // get the name from the left-hand-side and update the value
162 std::string lhsValue;
163 if (runObjLHS->hasProperty(firstSuggestion)) {
164 propName = firstSuggestion;
165 lhsValue = runObjLHS->getProperty(firstSuggestion)->value();
166 } else if (runObjLHS->hasProperty(secondSuggestion)) {
167 propName = secondSuggestion;
168 lhsValue = runObjLHS->getProperty(secondSuggestion)->value();
169 }
170
171 if (lhsValue.empty()) {
172 propValue = rhsValue;
173 } else if (rhsValue.empty()) {
174 propValue = lhsValue;
175 } else {
176 if (firstSuggestion == "start_time") {
177 // take the minimum time of the two
178 try {
179 auto value = std::min(DateAndTime(lhsValue), DateAndTime(rhsValue));
180 propValue = value.toISO8601String();
181 } catch (std::invalid_argument &) {
182 propValue = lhsValue + rhsValue; // simply concatenate strings
183 }
184 } else { // assume it is the end time
185 // take the maximum time of the two
186 try {
187 auto value = std::max(DateAndTime(lhsValue), DateAndTime(rhsValue));
188 propValue = value.toISO8601String();
189 } catch (std::invalid_argument &) {
190 propValue = lhsValue + rhsValue; // simply concatenate strings
191 }
192 }
193 }
194}
195
196} // namespace
197
205 // combine the two TimeROI if either is non-empty
206 if ((!m_timeroi->useAll()) || (!rhs.m_timeroi->useAll())) {
207 TimeROI combined(*m_timeroi);
208 // set this start/end time as the only ROI if it is empty
209 if (combined.useAll()) {
210 combined.addROI(this->startTime(), this->endTime());
211 }
212
213 // fixup the timeroi from the other
214 TimeROI rightROI(*rhs.m_timeroi);
215 if (rightROI.useAll() && rhs.hasStartTime() && rhs.hasEndTime()) {
216 rightROI.addROI(rhs.startTime(), rhs.endTime());
217 }
218
219 // replace the values accordingly
220 combined.update_union(rightROI);
221 this->m_timeroi->replaceROI(combined);
222 }
223
224 // determine the new start/end times
225 std::string startTimePropName;
226 std::string startTimePropValue;
227 std::string endTimePropName;
228 std::string endTimePropValue;
229 findAndConcatenateTimeStrProp(this, &rhs, "start_time", "start_run", startTimePropName, startTimePropValue);
230 findAndConcatenateTimeStrProp(this, &rhs, "end_time", "run_end", endTimePropName, endTimePropValue);
231
232 // merge and copy properties where there is no risk of corrupting data
233 mergeMergables(*m_manager, *rhs.m_manager);
234
235 // Other properties are added together if they are on the approved list
236 for (const auto &addableProp : ADDABLE) {
237 const std::string name = addableProp.name;
238 if (rhs.m_manager->existsProperty(name)) {
239 // get a pointer to the property on the right-hand side workspace
240 const Property *right = rhs.m_manager->getProperty(name);
241
242 // now deal with the left-hand side
243 if (m_manager->existsProperty(name)) {
244 Property *left = m_manager->getProperty(name);
245 if (!addableProp.opFunc) {
246 left->operator+=(right);
247 } else {
248 addableProp.opFunc(left, right);
249 }
250 } else
251 // no property on the left-hand side, create one and copy the
252 // right-hand side across verbatim
253 m_manager->declareProperty(std::unique_ptr<Property>(right->clone()), "");
254 }
255 }
256
257 // update the start/end times - this assumes that if either property was missing from the left, it was added during
258 // the mergeMergables step above
259 if (!startTimePropName.empty()) {
260 Property *prop = m_manager->getProperty(startTimePropName);
261 prop->setValue(startTimePropValue);
262 }
263 if (!endTimePropName.empty()) {
264 Property *prop = m_manager->getProperty(endTimePropName);
265 prop->setValue(endTimePropValue);
266 }
267
268 return *this;
269}
270
271// this overrides the one from LogManager so the proton charge can be recalculated
272void Run::setTimeROI(const Kernel::TimeROI &timeroi) {
273 LogManager::setTimeROI(timeroi);
274 this->integrateProtonCharge();
275 this->setDuration(); // update log "duration" with the duration of the new timeroi
276}
277
278//-----------------------------------------------------------------------------------------------
283void Run::setProtonCharge(const double charge) {
284 const std::string PROTON_CHARGE_UNITS("uA.hour");
286 addProperty(PROTON_CHARGE_LOG_NAME, charge, PROTON_CHARGE_UNITS);
287 } else {
289 charge_prop->setValue(boost::lexical_cast<std::string>(charge));
290 charge_prop->setUnits(PROTON_CHARGE_UNITS);
291 }
292}
293
294//-----------------------------------------------------------------------------------------------
300double Run::getProtonCharge() const {
301 double charge = 0.0;
302
303 if (!m_manager->existsProperty(PROTON_CHARGE_LOG_NAME) && !this->hasProperty("proton_charge")) {
304 g_log.notice() << "There is no proton charge associated with this workspace" << std::endl;
305 return charge;
306 }
307
308 if (!m_manager->existsProperty(PROTON_CHARGE_LOG_NAME)) {
310 } else if (m_manager->existsProperty(PROTON_CHARGE_UNFILTERED_LOG_NAME) &&
311 m_manager->getProperty(PROTON_CHARGE_UNFILTERED_LOG_NAME).operator int()) {
312 const std::vector<double> &protonChargeByPeriod = m_manager->getProperty("proton_charge_by_period");
313 const int currentPeriod = m_manager->getProperty("current_period");
314 m_manager->setProperty(PROTON_CHARGE_LOG_NAME, protonChargeByPeriod[currentPeriod - 1]);
316 }
317
318 if (m_manager->existsProperty(PROTON_CHARGE_LOG_NAME)) {
319 charge = m_manager->getProperty(PROTON_CHARGE_LOG_NAME);
320 } else {
321 g_log.warning() << PROTON_CHARGE_LOG_NAME << " log was not found. Proton Charge set to 0.0\n";
322 }
323 return charge;
324}
325
326//-----------------------------------------------------------------------------------------------
333void Run::integrateProtonCharge(const std::string &logname) const {
334 Kernel::TimeSeriesProperty<double> const *log = nullptr;
335
336 if (this->hasProperty(logname)) {
337 try {
338 log = dynamic_cast<Kernel::TimeSeriesProperty<double> const *>(this->getProperty(logname));
339 } catch (Exception::NotFoundError &) {
340 g_log.warning(logname + " log was not found. The value of the total proton "
341 "charge has not been set");
342 return;
343 }
344 }
345
346 if (log) {
347 // start with a clearly nonsense accumulated value
348 double total;
349
350 // get a copy of the run's TimeROI for selecting values
351 Kernel::TimeROI timeroi = this->getTimeROI();
352 // If the proton charge series is filtered, fetch its TimeROI and use it to update `timeRoi`
353 if (const auto *filteredLog =
354 dynamic_cast<Kernel::FilteredTimeSeriesProperty<double> *>(this->getProperty(logname)))
355 timeroi.update_or_replace_intersection(filteredLog->getTimeROI());
356
357 if (timeroi.useAll()) {
358 // simple accumulation
359 const std::vector<double> logValues = log->valuesAsVector();
360 total = std::accumulate(logValues.begin(), logValues.end(), 0.0);
361 } else {
362 const auto &times = log->timesAsVector();
363
364 total = std::accumulate(times.cbegin(), times.cend(), 0., [&](double valueTotal, const DateAndTime &time) {
365 return timeroi.valueAtTime(time) ? valueTotal + log->getSingleValue(time) : valueTotal;
366 });
367 }
368
369 const std::string &unit = log->units();
370 // Do we need to take account of a unit
371 if (unit.find("picoCoulomb") != std::string::npos) {
373 const double currentConversion = 1.e-6 / 3600.;
374 total *= currentConversion;
375 } else if (!unit.empty() && unit != "uAh") {
376 g_log.warning(logname + " log has units other than uAh or "
377 "picoCoulombs. The value of the total proton charge has "
378 "been left at the sum of the log values.");
379 }
380 const_cast<Run *>(this)->setProtonCharge(total);
381 // Mark gd_prtn_chrg as filtered as this method accounts for period filtering
382 if (m_manager->existsProperty(PROTON_CHARGE_UNFILTERED_LOG_NAME)) {
384 }
385
386 } else {
387 g_log.warning(logname + " log was not a time series property. The value of the total proton "
388 "charge has not been set");
389 }
390}
391
392//-----------------------------------------------------------------------------------------------
398std::tuple<double, double, double> Run::getBadPulseRange(const std::string &logname, const double &cutoff) const {
399 // check the proton charge exists in the run object
400 if (!this->hasProperty(logname)) {
401 throw std::runtime_error("Failed to find \"" + logname + "\" in sample logs");
402 }
403 const auto *pcharge_log = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(this->getLogData(logname));
404 if (!pcharge_log) {
405 throw std::logic_error("Failed to find \"" + logname + "\" in sample logs");
406 }
407 Kernel::TimeSeriesPropertyStatistics stats = pcharge_log->getStatistics();
408
409 // check that the maximum value is greater than zero
410 if (stats.maximum <= 0.) {
411 throw std::runtime_error("Maximum value of charge is not greater than zero (" + logname + ")");
412 }
413
414 // set the range
415 const double min_pcharge = stats.mean * cutoff * 0.01;
416 const double max_pcharge = stats.maximum * 1.1; // make sure everything high is in
417 if (min_pcharge >= max_pcharge) {
418 throw std::runtime_error("proton_charge window filters out all of the data");
419 }
420 return {min_pcharge, max_pcharge, stats.mean};
421}
422
423//-----------------------------------------------------------------------------------------------
430 if (m_timeroi->useAll())
431 return;
432 double duration{m_timeroi->durationInSeconds()};
433 const std::string NAME("duration");
434 const std::string UNITS("second");
435 if (!hasProperty(NAME))
436 addProperty(NAME, duration, UNITS);
437 else {
438 Kernel::Property *prop = getProperty(NAME);
439 prop->setValue(boost::lexical_cast<std::string>(duration));
440 prop->setUnits(UNITS);
441 }
442}
443
444//-----------------------------------------------------------------------------------------------
452void Run::storeHistogramBinBoundaries(const std::vector<double> &histoBins) {
453 if (histoBins.size() < 2) {
454 std::ostringstream os;
455 os << "Run::storeEnergyBinBoundaries - Fewer than 2 values given, size=" << histoBins.size()
456 << ". Cannot interpret values as bin boundaries.";
457 throw std::invalid_argument(os.str());
458 }
459 if (histoBins.front() >= histoBins.back()) {
460 std::ostringstream os;
461 os << "Run::storeEnergyBinBoundaries - Inconsistent start & end values "
462 "given, size="
463 << histoBins.size() << ". Cannot interpret values as bin boundaries.";
464 throw std::out_of_range(os.str());
465 }
466 m_histoBins = histoBins;
467}
468
476std::pair<double, double> Run::histogramBinBoundaries(const double value) const {
477 if (m_histoBins.empty()) {
478 throw std::runtime_error("Run::histogramBoundaries - No energy bins have "
479 "been stored for this run");
480 }
481
482 if (value < m_histoBins.front()) {
483 std::ostringstream os;
484 os << "Run::histogramBinBoundaries- Value lower than first bin boundary. "
485 "Value= "
486 << value << ", first boundary=" << m_histoBins.front();
487 throw std::out_of_range(os.str());
488 }
489 if (value > m_histoBins.back()) {
490 std::ostringstream os;
491 os << "Run::histogramBinBoundaries- Value greater than last bin boundary. "
492 "Value= "
493 << value << ", last boundary=" << m_histoBins.back();
494 throw std::out_of_range(os.str());
495 }
496 const auto index = static_cast<std::size_t>(VectorHelper::getBinIndex(m_histoBins, value));
497 return std::make_pair(m_histoBins[index], m_histoBins[index + 1]);
498}
499
505std::vector<double> Run::getBinBoundaries() const {
506 if (m_histoBins.empty()) {
507 throw std::runtime_error("Run::histogramBoundaries - No energy bins have "
508 "been stored for this run");
509 }
510
511 return m_histoBins;
512}
513
514//-----------------------------------------------------------------------------------------------
517size_t Run::getMemorySize() const {
518 size_t total = LogManager::getMemorySize();
519 total += sizeof(Geometry::Goniometer) * m_goniometers.size();
520 total += m_histoBins.size() * sizeof(double);
521 return total;
522}
523
526
529
530//-----------------------------------------------------------------------------------------------
537void Run::setGoniometer(const Geometry::Goniometer &goniometer, const bool useLogValues) {
538 auto old = std::move(m_goniometers);
539 try {
540 m_goniometers.emplace_back(std::make_unique<Geometry::Goniometer>(goniometer));
541 if (useLogValues)
543 } catch (std::runtime_error &) {
544 m_goniometers = std::move(old);
545 throw;
546 }
547}
548
549//-----------------------------------------------------------------------------------------------
555 auto old = std::move(m_goniometers);
556 try {
557 calculateGoniometerMatrices(goniometer);
558 } catch (std::runtime_error &) {
559 m_goniometers = std::move(old);
560 throw;
561 }
562}
563
571
572//-----------------------------------------------------------------------------------------------
574size_t Run::getNumGoniometers() const { return m_goniometers.size(); }
575
576//-----------------------------------------------------------------------------------------------
582size_t Run::addGoniometer(const Geometry::Goniometer &goniometer) {
583 m_goniometers.emplace_back(std::make_unique<Geometry::Goniometer>(goniometer));
584 return m_goniometers.size() - 1;
585}
586
587//-----------------------------------------------------------------------------------------------
590
591//-----------------------------------------------------------------------------------------------
598 if (index >= m_goniometers.size())
599 throw std::out_of_range("Run::getGoniometer() const: index is out of range.");
600 return *m_goniometers[index];
601}
602
603//-----------------------------------------------------------------------------------------------
610 if (index >= m_goniometers.size())
611 throw std::out_of_range("Run::getGoniometer() const: index is out of range.");
612 return *m_goniometers[index];
613}
614
623 if (index >= m_goniometers.size())
624 throw std::out_of_range("Run::getGoniometer() const: index is out of range.");
625 return m_goniometers[index]->getR();
626}
627
632const std::vector<Kernel::Matrix<double>> Run::getGoniometerMatrices() const {
633 std::vector<Kernel::Matrix<double>> goniometers;
634 goniometers.reserve(m_goniometers.size());
635 for (auto it = m_goniometers.begin(); it != m_goniometers.end(); ++it) {
636 goniometers.emplace_back((*it)->getR());
637 }
638 return goniometers;
639}
640
641//--------------------------------------------------------------------------------------------
647void Run::saveNexus(Nexus::File *file, const std::string &group, bool keepOpen) const {
648 LogManager::saveNexus(file, group, true);
649
650 // write the goniometer
651 if (m_goniometers.size() == 1)
652 m_goniometers[0]->saveNexus(file, GONIOMETER_LOG_NAME);
653 else if (m_goniometers.size() > 1) {
654 file->makeGroup(GONIOMETERS_LOG_NAME, "NXcollection", true);
655 file->writeData("num_goniometer", int(m_goniometers.size()));
656 for (size_t i = 0; i < m_goniometers.size(); i++) {
657 m_goniometers[i]->saveNexus(file, "goniometer" + std::to_string(i));
658 }
659 file->closeGroup();
660 }
661
662 // write the histogram bins, if there are any
663 if (!m_histoBins.empty()) {
664 file->makeGroup(HISTO_BINS_LOG_NAME, "NXdata", true);
665 file->writeData("value", m_histoBins);
666 file->closeGroup();
667 }
668 if (this->hasProperty("PeakRadius")) {
669 const std::vector<double> &values = this->getPropertyValueAsType<std::vector<double>>("PeakRadius");
670
671 file->makeGroup(PEAK_RADIUS_GROUP, "NXdata", true);
672 file->writeData("value", values);
673 file->closeGroup();
674 }
675 if (this->hasProperty("BackgroundInnerRadius")) {
676 file->makeGroup(INNER_BKG_RADIUS_GROUP, "NXdata", true);
677 const std::vector<double> &values = this->getPropertyValueAsType<std::vector<double>>("BackgroundInnerRadius");
678 file->writeData("value", values);
679 file->closeGroup();
680 }
681 if (this->hasProperty("BackgroundOuterRadius")) {
682 file->makeGroup(OUTER_BKG_RADIUS_GROUP, "NXdata", true);
683 const std::vector<double> &values = this->getPropertyValueAsType<std::vector<double>>("BackgroundOuterRadius");
684 file->writeData("value", values);
685 file->closeGroup();
686 }
687 if (!keepOpen)
688 file->closeGroup();
689}
690
691//--------------------------------------------------------------------------------------------
700void Run::loadNexus(Nexus::File *file, const std::string &group, const Nexus::NexusDescriptor &fileInfo,
701 const std::string &prefix, bool keepOpen) {
702
703 if (!group.empty()) {
704 file->openGroup(group, "NXgroup");
705 }
706
707 // Example: /MDEventWorkspace/experiment4 + / + logs
708 const std::string absoluteGroupName = prefix + "/" + group;
709 LogManager::loadNexus(file, fileInfo, absoluteGroupName);
710
711 // group hierarchy levels
712 const auto levels = std::count(absoluteGroupName.begin(), absoluteGroupName.end(), '/');
713
714 const auto &allEntries = fileInfo.getAllEntries();
715 // loop through nxClass sets
716 for (const auto &nxClassPair : allEntries) {
717 const std::set<std::string> &nxClassEntries = nxClassPair.second;
718
719 // since std::set is ordered, just find the iterators
720 // for the bounds of the current experiment number
721 // take advantage of the fact that std::set is sorted, find by prefix bounds
722 auto itLower = nxClassEntries.lower_bound(absoluteGroupName);
723 // not prefixed
724 if (itLower == nxClassEntries.end()) {
725 continue;
726 }
727 if (itLower->compare(0, absoluteGroupName.size(), absoluteGroupName) != 0) {
728 continue;
729 }
730
731 // loop through the set with prefix absoluteGroupName
732 for (auto it = itLower;
733 it != nxClassEntries.end() && it->compare(0, absoluteGroupName.size(), absoluteGroupName) == 0; ++it) {
734
735 // only next level entries
736 const std::string &absoluteEntryName = *it;
737 if (std::count(absoluteEntryName.begin(), absoluteEntryName.end(), '/') != levels + 1) {
738 continue;
739 }
740 const std::string nameClass = absoluteEntryName.substr(absoluteEntryName.find_last_of('/') + 1);
741 loadNexusCommon(file, nameClass);
742 }
743 }
744
745 if (!(group.empty() || keepOpen))
746 file->closeGroup();
747
748 if (this->hasProperty("proton_charge")) {
749 // Old files may have a proton_charge field, single value.
750 // Modern files (e.g. SNS) have a proton_charge TimeSeriesProperty.
751 if (const auto *charge_log = dynamic_cast<PropertyWithValue<double> *>(this->getProperty("proton_charge"))) {
752 this->setProtonCharge(boost::lexical_cast<double>(charge_log->value()));
753 }
754 }
755}
756
757//--------------------------------------------------------------------------------------------
765void Run::loadNexus(Nexus::File *file, const std::string &group, bool keepOpen) {
766
767 if (!group.empty()) {
768 file->openGroup(group, "NXgroup");
769 }
770 std::map<std::string, std::string> entries;
771 file->getEntries(entries);
772 LogManager::loadNexus(file, entries);
773 for (const auto &name_class : entries) {
774 loadNexusCommon(file, name_class.first);
775 }
776 if (!(group.empty() || keepOpen))
777 file->closeGroup();
778
779 if (this->hasProperty("proton_charge")) {
780 // Old files may have a proton_charge field, single value.
781 // Modern files (e.g. SNS) have a proton_charge TimeSeriesProperty.
782 if (const auto *charge_log = dynamic_cast<PropertyWithValue<double> *>(this->getProperty("proton_charge"))) {
783 this->setProtonCharge(boost::lexical_cast<double>(charge_log->value()));
784 }
785 }
786}
787
788//-----------------------------------------------------------------------------------------------------------------------
789// Private methods
790//-----------------------------------------------------------------------------------------------------------------------
791
796 for (size_t i = 0; i < m_goniometers[0]->getNumberAxes(); ++i) {
797 const std::string axisName = m_goniometers[0]->getAxis(i).name;
798 auto stats = getStatistics(axisName);
799 const double minAngle = stats.minimum;
800 const double maxAngle = stats.maximum;
801 const double angle = stats.time_mean;
802
803 if (minAngle != maxAngle && !(std::isnan(minAngle) && std::isnan(maxAngle))) {
804 const double lastAngle = getLogAsSingleValue(axisName, Kernel::Math::LastValue);
805 g_log.warning("Goniometer angle changed in " + axisName + " log from " +
806 boost::lexical_cast<std::string>(minAngle) + " to " + boost::lexical_cast<std::string>(maxAngle) +
807 ". Used time averaged value = " + boost::lexical_cast<std::string>(angle) + ".");
808 if (axisName == "omega") {
809 g_log.warning("To set to last angle, replace omega with " + boost::lexical_cast<std::string>(lastAngle) +
810 ": "
811 "SetGoniometer(Workspace=\'workspace\',Axis0=omega,0,1,0,"
812 "1\',Axis1='chi,0,0,1,1',Axis2='phi,0,1,0,1')");
813 } else if (axisName == "chi") {
814 g_log.warning("To set to last angle, replace chi with " + boost::lexical_cast<std::string>(lastAngle) +
815 ": "
816 "SetGoniometer(Workspace=\'workspace\',Axis0=omega,0,1,0,"
817 "1\',Axis1='chi,0,0,1,1',Axis2='phi,0,1,0,1')");
818 } else if (axisName == "phi") {
819 g_log.warning("To set to last angle, replace phi with " + boost::lexical_cast<std::string>(lastAngle) +
820 ": "
821 "SetGoniometer(Workspace=\'workspace\',Axis0=omega,0,1,0,"
822 "1\',Axis1='chi,0,0,1,1',Axis2='phi,0,1,0,1')");
823 }
824 }
825 m_goniometers[0]->setRotationAngle(i, angle);
826 }
827}
828
834 if (goniometer.getNumberAxes() == 0)
835 throw std::runtime_error("Run::calculateGoniometerMatrices must include axes for goniometer");
836
837 const size_t num_log_values = getTimeSeriesProperty<double>(goniometer.getAxis(0).name)->size();
838
839 m_goniometers.clear();
840 m_goniometers.reserve(num_log_values);
841
842 for (size_t i = 0; i < num_log_values; ++i)
843 m_goniometers.emplace_back(std::make_unique<Geometry::Goniometer>(goniometer));
844
845 for (size_t i = 0; i < goniometer.getNumberAxes(); ++i) {
846 const auto angles = getTimeSeriesProperty<double>(goniometer.getAxis(i).name)->valuesAsVector();
847 if (angles.size() != num_log_values)
848 throw std::runtime_error("Run::calculateGoniometerMatrices different "
849 "number of log entries between axes");
850
851 for (size_t j = 0; j < num_log_values; ++j) {
852 m_goniometers[j]->setRotationAngle(i, angles[j]);
853 }
854 }
855}
856
863 // get pointers to all the properties on the right-handside and prepare to
864 // loop through them
865 const std::vector<Property *> &inc = toAdd.getProperties();
866 for (auto ptr : inc) {
867 const std::string &rhs_name = ptr->name();
868 try {
869 // now get pointers to the same properties on the left-handside
870 Property *lhs_prop(sum.getProperty(rhs_name));
871 lhs_prop->merge(ptr);
872 } catch (Exception::NotFoundError &) {
873 // copy any properties that aren't already on the left hand side
874 auto copy = std::unique_ptr<Property>(ptr->clone());
875 // And we add a copy of that property to *this
876 sum.declareProperty(std::move(copy), "");
877 }
878 }
879}
880
881//-----------------------------------------------------------------------------------------------
884void Run::copyGoniometers(const Run &other) {
885 m_goniometers.clear();
886 m_goniometers.reserve(other.m_goniometers.size());
887 for (const auto &goniometer : other.m_goniometers) {
888 auto new_goniometer = std::make_unique<Geometry::Goniometer>(*goniometer);
889 m_goniometers.emplace_back(std::move(new_goniometer));
890 }
891}
892
893void Run::loadNexusCommon(Nexus::File *file, const std::string &nameClass) {
894 if (nameClass == GONIOMETER_LOG_NAME) {
895 // Goniometer class
896 m_goniometers[0]->loadNexus(file, nameClass);
897 } else if (nameClass == GONIOMETERS_LOG_NAME) {
898 file->openGroup(nameClass, "NXcollection");
899 int num_goniometer;
900 file->readData("num_goniometer", num_goniometer);
901 m_goniometers.clear();
902 m_goniometers.reserve(num_goniometer);
903 for (int i = 0; i < num_goniometer; i++) {
904 m_goniometers.emplace_back(std::make_unique<Geometry::Goniometer>());
905 m_goniometers[i]->loadNexus(file, "goniometer" + std::to_string(i));
906 }
907 file->closeGroup();
908 } else if (nameClass == HISTO_BINS_LOG_NAME) {
909 file->openGroup(nameClass, "NXdata");
910 file->readData("value", m_histoBins);
911 file->closeGroup();
912 } else if (nameClass == PEAK_RADIUS_GROUP) {
913 file->openGroup(nameClass, "NXdata");
914 std::vector<double> values;
915 file->readData("value", values);
916 file->closeGroup();
917 this->addProperty("PeakRadius", values, true);
918 } else if (nameClass == INNER_BKG_RADIUS_GROUP) {
919 file->openGroup(nameClass, "NXdata");
920 std::vector<double> values;
921 file->readData("value", values);
922 file->closeGroup();
923 this->addProperty("BackgroundInnerRadius", values, true);
924 } else if (nameClass == OUTER_BKG_RADIUS_GROUP) {
925 file->openGroup(nameClass, "NXdata");
926 std::vector<double> values;
927 file->readData("value", values);
928 file->closeGroup();
929 this->addProperty("BackgroundOuterRadius", values, true);
930 } else if (nameClass == "proton_charge" && !this->hasProperty("proton_charge")) {
931 // Old files may have a proton_charge field, single value (not even NXlog)
932 double charge;
933 file->readData("proton_charge", charge);
934 this->setProtonCharge(charge);
935 } else if (nameClass == "proton_charge_by_period") {
936 file->openGroup(nameClass, "NXlog");
937 std::vector<double> values;
938 file->readData("value", values);
939 file->closeGroup();
940 this->addProperty("proton_charge_by_period", values, true);
941 }
942}
943
944} // namespace Mantid::API
func opFunc
Definition Run.cpp:61
std::string name
Definition Run.cpp:60
const std::vector< double > & rhs
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
double left
double right
This class contains the information about the log entries.
Definition LogManager.h:44
virtual size_t getMemorySize() const
Return an approximate memory size for the object in bytes.
const Types::Core::DateAndTime endTime() const
Return the run end time.
virtual void setTimeROI(const Kernel::TimeROI &timeroi)
bool hasProperty(const std::string &name) const
Does the property exist on the object.
const Kernel::TimeROI & getTimeROI() const
const std::vector< Kernel::Property * > & getLogData() const
Access all log entries.
Definition LogManager.h:146
std::unique_ptr< Kernel::PropertyManager > m_manager
A pointer to a property manager.
Definition LogManager.h:217
Kernel::TimeSeriesPropertyStatistics getStatistics(const std::string &name) const
Returns various statistics computations for a given property.
const Types::Core::DateAndTime startTime() const
Return the run start time.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
virtual void filterByTime(const Types::Core::DateAndTime start, const Types::Core::DateAndTime stop)
Filter the logs by time.
static const std::string PROTON_CHARGE_LOG_NAME
Name of the log entry containing the proton charge when retrieved using getProtonCharge.
Definition LogManager.h:221
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
virtual void saveNexus(Nexus::File *file, const std::string &group, bool keepOpen=false) const
Save the run to a NeXus file with a given group name.
LogManager & operator=(const LogManager &other)
bool operator==(const LogManager &other) const
std::unique_ptr< Kernel::TimeROI > m_timeroi
Definition LogManager.h:218
double getLogAsSingleValue(const std::string &name, Kernel::Math::StatisticType statistic=Kernel::Math::Mean) const
Definition LogManager.h:161
static const std::string PROTON_CHARGE_UNFILTERED_LOG_NAME
Flag to signify if a filter has been applied to the proton charge log.
Definition LogManager.h:223
virtual void loadNexus(Nexus::File *file, const std::string &group, const Mantid::Nexus::NexusDescriptor &fileInfo, const std::string &prefix, bool keepOpen=false)
Load the run from a NeXus file with a given group name. Overload that uses NexusDescriptor for faster...
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
std::tuple< double, double, double > getBadPulseRange(const std::string &logname="proton_charge", const double &cutoff=95.) const
determine the range of bad pulses to filter
Definition Run.cpp:398
void storeHistogramBinBoundaries(const std::vector< double > &histoBins)
Store the given values as a set of histogram bin boundaries.
Definition Run.cpp:452
bool operator!=(const Run &other)
Definition Run.cpp:118
void setProtonCharge(const double charge)
Set the proton charge.
Definition Run.cpp:283
bool operator==(const Run &other)
Definition Run.cpp:108
Run & operator+=(const Run &rhs)
Addition.
Definition Run.cpp:204
void integrateProtonCharge(const std::string &logname="proton_charge") const
Integrate the proton charge over the whole run time - default log proton_charge.
Definition Run.cpp:333
void setDuration()
update property "duration" with the duration of the Run's TimeROI attribute
Definition Run.cpp:429
void copyGoniometers(const Run &other)
Copy the goniometers from another.
Definition Run.cpp:884
std::vector< double > getBinBoundaries() const
Returns the vector of bin boundaries.
Definition Run.cpp:505
std::vector< std::unique_ptr< Geometry::Goniometer > > m_goniometers
Goniometer for this run.
Definition Run.h:124
size_t getNumGoniometers() const
Get the number of goniometers in the Run.
Definition Run.cpp:574
void clearGoniometers()
Clear all goniometers on the Run.
Definition Run.cpp:589
void calculateGoniometerMatrices(const Geometry::Goniometer &goniometer)
Calculate the gonoimeter matrices from logs.
Definition Run.cpp:833
void setGoniometer(const Geometry::Goniometer &goniometer, const bool useLogValues)
Set a single gonoimeter & read the average values from the logs if told to do so.
Definition Run.cpp:537
void calculateAverageGoniometerMatrix()
Calculate the average gonoimeter matrix.
Definition Run.cpp:795
Geometry::Goniometer & mutableGoniometer()
Return reference to the first non-const Goniometer object for this run.
Definition Run.cpp:528
const Geometry::Goniometer & getGoniometer() const
Return reference to the first const Goniometer object for this run.
Definition Run.cpp:525
size_t getMemorySize() const override
Return an approximate memory size for the object in bytes.
Definition Run.cpp:517
const Kernel::Matrix< double > & getGoniometerMatrix() const
Retrieve the first goniometer rotation matrix.
Definition Run.cpp:570
void filterByTime(const Types::Core::DateAndTime start, const Types::Core::DateAndTime stop) override
Filter the logs by time.
Definition Run.cpp:141
size_t addGoniometer(const Geometry::Goniometer &goniometer)
Append a goniometer to the run.
Definition Run.cpp:582
void setTimeROI(const Kernel::TimeROI &timeroi) override
Definition Run.cpp:272
std::shared_ptr< Run > clone()
Clone.
Definition Run.cpp:120
void loadNexusCommon(Nexus::File *file, const std::string &nameClass)
Definition Run.cpp:893
double getProtonCharge() const
Get the proton charge.
Definition Run.cpp:300
void saveNexus(Nexus::File *file, const std::string &group, bool keepOpen=false) const override
Save the run to a NeXus file with a given group name.
Definition Run.cpp:647
void mergeMergables(Mantid::Kernel::PropertyManager &sum, const Mantid::Kernel::PropertyManager &toAdd)
Adds all the time series in from one property manager into another.
Definition Run.cpp:862
std::pair< double, double > histogramBinBoundaries(const double value) const
Returns the bin boundaries for a given value.
Definition Run.cpp:476
void setGoniometers(const Geometry::Goniometer &goniometer)
Set the gonoimeters using the individual values.
Definition Run.cpp:554
Run & operator=(const Run &other)
Definition Run.cpp:99
const std::vector< Kernel::Matrix< double > > getGoniometerMatrices() const
Get vector of all goniometer matrices in the Run.
Definition Run.cpp:632
std::vector< double > m_histoBins
A set of histograms that can be stored here for future reference.
Definition Run.h:126
void loadNexus(Nexus::File *file, const std::string &group, const Mantid::Nexus::NexusDescriptor &fileInfo, const std::string &prefix, bool keepOpen=false) override
Load the run from a NeXus file with a given group name.
Definition Run.cpp:700
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
const GoniometerAxis & getAxis(size_t axisnumber) const
Get GoniometerAxis obfject using motor number.
Exception for when an item is not found in a collection.
Definition Exception.h:145
Templated class that defines a filtered time series but still gives access to the original data.
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
Property manager helper class.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
void declareProperty(std::unique_ptr< Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
const std::vector< Property * > & getProperties() const override
Get the list of managed properties.
The concrete, templated class for properties.
Base class for properties.
Definition Property.h:94
virtual const std::string & units() const
Returns the units of the property, if any, as a string.
Definition Property.cpp:191
virtual std::string setValue(const std::string &)=0
Set the value of the property via a string.
virtual void setUnits(const std::string &unit)
Sets the units of the property, as a string.
Definition Property.cpp:198
virtual Property & merge(Property *)
Just returns the property (*this) unless overridden.
Definition Property.h:193
virtual std::string value() const =0
Returns the value of the property as a string.
TimeROI : Object that holds information about when the time measurement was active.
Definition TimeROI.h:18
void update_union(const TimeROI &other)
Updates the TimeROI values with the union with another TimeROI.
Definition TimeROI.cpp:529
void addROI(const std::string &startTime, const std::string &stopTime)
Definition TimeROI.cpp:76
bool useAll() const
TimeROI selects all time to be used.
Definition TimeROI.cpp:693
void update_or_replace_intersection(const TimeROI &other)
If this is empty, replace it with the supplied TimeROI, otherwise calculate the intersection.
Definition TimeROI.cpp:548
A specialised Property class for holding a series of time-value pairs.
std::vector< TYPE > valuesAsVector() const
Return the time series's values (unfiltered) as a vector<TYPE>
std::vector< Types::Core::DateAndTime > timesAsVector() const override
Return the time series's times as a vector<DateAndTime>
const std::map< std::string, std::set< std::string > > & getAllEntries() const noexcept
Returns a const reference of the internal map holding all entries in the Nexus HDF5 file.
Kernel::Logger g_log("ExperimentInfo")
static logger object
MANTID_KERNEL_DLL int getBinIndex(const std::vector< double > &bins, const double value)
Return the index into a vector of bin boundaries for a particular X value.
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
Struct holding some useful statistics for a TimeSeriesProperty.