Mantid
Loading...
Searching...
No Matches
LoadNexusLogs.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 +
10#include "MantidAPI/Run.h"
15
16#include <Poco/DateTimeFormat.h>
17#include <Poco/DateTimeFormatter.h>
18#include <Poco/DateTimeParser.h>
19#include <Poco/Glob.h>
20#include <boost/scoped_array.hpp>
21
22#include <algorithm>
23#include <locale>
24
25namespace Mantid::DataHandling {
26// Register the algorithm into the algorithm factory
27DECLARE_ALGORITHM(LoadNexusLogs)
28
29using namespace Kernel;
30using API::FileProperty;
31using API::MatrixWorkspace;
33using API::WorkspaceProperty;
34using std::size_t;
35using Types::Core::DateAndTime;
36
37// Anonymous namespace
38namespace {
45bool loadAndApplyMeasurementInfo(Nexus::File *const file, API::MatrixWorkspace &workspace) {
46
47 bool successfullyApplied = false;
48 try {
49 file->openGroup("measurement", "NXcollection");
50
51 // If we can open the measurement group. We assume that the following will
52 // be available.
53 file->openData("id");
54 workspace.mutableRun().addLogData(
55 new Mantid::Kernel::PropertyWithValue<std::string>("measurement_id", file->getStrData()));
56 file->closeData();
57 file->openData("label");
58 workspace.mutableRun().addLogData(
59 new Mantid::Kernel::PropertyWithValue<std::string>("measurement_label", file->getStrData()));
60 file->closeData();
61 file->openData("subid");
62 workspace.mutableRun().addLogData(
63 new Mantid::Kernel::PropertyWithValue<std::string>("measurement_subid", file->getStrData()));
64 file->closeData();
65 file->openData("type");
66 workspace.mutableRun().addLogData(
67 new Mantid::Kernel::PropertyWithValue<std::string>("measurement_type", file->getStrData()));
68 file->closeData();
69 file->closeGroup();
70 successfullyApplied = true;
71 } catch (Nexus::Exception const &) {
72 successfullyApplied = false;
73 }
74 return successfullyApplied;
75}
76
83bool loadAndApplyRunTitle(Nexus::File *const file, API::MatrixWorkspace &workspace) {
84
85 bool successfullyApplied = false;
86 try {
87 file->openData("title");
88 workspace.mutableRun().addLogData(
89 new Mantid::Kernel::PropertyWithValue<std::string>("run_title", file->getStrData()));
90 file->closeData();
91 successfullyApplied = true;
92 } catch (Nexus::Exception const &) {
93 successfullyApplied = false;
94 }
95 return successfullyApplied;
96}
97
111bool isControlValue(const char &c, const std::string &propName, Kernel::Logger &log) {
112 // Have to check it falls within range accepted by c style check
113 if (c <= -1) {
114 log.warning("Found an invalid character in property " + propName);
115 // Pretend this is a control value so it is sanitized
116 return true;
117 } else {
118 // Use default global c++ locale within this program
119 std::locale locale{};
120 // Use c++ style call so we don't need to cast from int to bool
121 return std::iscntrl(c, locale);
122 }
123}
124
137std::unique_ptr<Kernel::Property> createTimeSeries(Nexus::File &file, const std::string &propName,
138 const std::string &freqStart, Kernel::Logger &log) {
139 file.openData("time");
140 //----- Start time is an ISO8601 string date and time. ------
141 std::string start;
142 try {
143 file.getAttr("start", start);
144 } catch (Nexus::Exception const &) {
145 // Some logs have "offset" instead of start
146 try {
147 file.getAttr("offset", start);
148 } catch (Nexus::Exception const &) {
149 log.warning() << "Log entry has no start time indicated.\n";
150 file.closeData();
151 throw;
152 }
153 }
154 if (start == "No Time") {
155 start = freqStart;
156 }
157
158 // Convert to date and time
159 Types::Core::DateAndTime start_time = Types::Core::DateAndTime(start);
160 std::string time_units;
161 file.getAttr("units", time_units);
162 if (time_units.compare("second") < 0 && time_units != "s" &&
163 time_units != "minutes") // Can be s/second/seconds/minutes
164 {
165 file.closeData();
166 throw Nexus::Exception("Unsupported time unit '" + time_units + "'");
167 }
168 //--- Load the seconds into a double array ---
169 std::vector<double> time_double;
170 try {
171 file.getDataCoerce(time_double);
172 } catch (Nexus::Exception const &e) {
173 log.warning() << "Log entry's time field could not be loaded: '" << e.what() << "'.\n";
174 file.closeData();
175 throw;
176 }
177 file.closeData(); // Close time data
178 log.debug() << " done reading \"time\" array\n";
179
180 // Convert to seconds if needed
181 if (time_units == "minutes") {
182 using std::placeholders::_1;
183 std::transform(time_double.begin(), time_double.end(), time_double.begin(),
184 std::bind(std::multiplies<double>(), _1, 60.0));
185 }
186
187 // Now the values: Could be a string, int or double
188 file.openData("value");
189 // Get the units of the property
190 std::string value_units;
191 try {
192 file.getAttr("units", value_units);
193 } catch (Nexus::Exception const &) {
194 // Ignore missing units field.
195 value_units = "";
196 }
197
198 // Now the actual data
199 Nexus::Info info = file.getInfo();
200 // Check the size
201 if (size_t(info.dims[0]) != time_double.size()) {
202 file.closeData();
203 throw Nexus::Exception("Invalid value entry for time series");
204 }
205 if (file.isDataInt()) // Int type
206 {
207 std::vector<int> values;
208 try {
209 file.getDataCoerce(values);
210 file.closeData();
211 } catch (Nexus::Exception const &) {
212 file.closeData();
213 throw;
214 }
215 // Make an int TSP
216 auto tsp = std::make_unique<TimeSeriesProperty<int>>(propName);
217 tsp->create(start_time, time_double, values);
218 tsp->setUnits(value_units);
219 log.debug() << " done reading \"value\" array\n";
220 return tsp;
221 } else if (info.type == NXnumtype::CHAR) {
222 std::string values;
223 const int64_t item_length = info.dims[1];
224 try {
225 const int64_t nitems = info.dims[0];
226 const std::size_t total_length = std::size_t(nitems * item_length);
227 boost::scoped_array<char> val_array(new char[total_length]);
228 file.getData(val_array.get());
229 file.closeData();
230 values = std::string(val_array.get(), total_length);
231 } catch (Nexus::Exception const &) {
232 file.closeData();
233 throw;
234 }
235 // The string may contain non-printable (i.e. control) characters, replace these
236 std::replace_if(values.begin(), values.end(), [&](const char &c) { return isControlValue(c, propName, log); }, ' ');
237 auto tsp = std::make_unique<TimeSeriesProperty<std::string>>(propName);
238 std::vector<DateAndTime> times;
239 DateAndTime::createVector(start_time, time_double, times);
240 const size_t ntimes = times.size();
241 for (size_t i = 0; i < ntimes; ++i) {
242 std::string value_i = std::string(values.data() + i * item_length, item_length);
243 tsp->addValue(times[i], value_i);
244 }
245 tsp->setUnits(value_units);
246 log.debug() << " done reading \"value\" array\n";
247 return tsp;
248 } else if (info.type == NXnumtype::FLOAT32 || info.type == NXnumtype::FLOAT64) {
249 std::vector<double> values;
250 try {
251 file.getDataCoerce(values);
252 file.closeData();
253 } catch (Nexus::Exception const &) {
254 file.closeData();
255 throw;
256 }
257 auto tsp = std::make_unique<TimeSeriesProperty<double>>(propName);
258 tsp->create(start_time, time_double, values);
259 tsp->setUnits(value_units);
260 log.debug() << " done reading \"value\" array\n";
261 return tsp;
262 } else {
263 throw Nexus::Exception("Invalid value type for time series. Only int, double or strings are "
264 "supported");
265 }
266}
267
278std::unique_ptr<Kernel::Property> createTimeSeriesValidityFilter(Nexus::File &file, const Kernel::Property &prop,
279 Kernel::Logger &log) {
280 const auto tsProp = dynamic_cast<const Kernel::ITimeSeriesProperty *>(&prop);
281 const auto times = tsProp->timesAsVector();
282 std::vector<int> values;
283 std::vector<bool> boolValues;
284 // Now the validity of the values
285 // this should be a match int array to the data values (or times)
286 // If not present assume all data is valid
287 if (file.hasData("value_valid")) {
288 file.openData("value_valid");
289 try {
290 // Now the validity data
291 Nexus::Info info = file.getInfo();
292 // Check the size
293 if (size_t(info.dims[0]) != times.size()) {
294 throw Nexus::Exception("Invalid value entry for validity data");
295 }
296 if (file.isDataInt()) // Int type
297 {
298 try {
299 file.getDataCoerce(values);
300 file.closeData();
301 } catch (Nexus::Exception const &) {
302 throw;
303 }
304 } else {
305 throw Nexus::Exception("Invalid value type for validity data. Only int is supported");
306 }
307 } catch (std::exception const &ex) {
308 std::string error_msg = ex.what();
309 log.warning() << error_msg << "\n";
310 file.closeData();
311 // no data found
312 return std::unique_ptr<Kernel::Property>(nullptr);
313 }
314 }
315
316 bool invalidDataFound = false;
317 boolValues.reserve(values.size());
318 // convert the integer values to boolean with 0=invalid data
319 for (size_t i = 0; i < values.size(); i++) {
320 bool isInvalidData = (values[i] == 0);
321 boolValues.emplace_back(!isInvalidData);
322 if (isInvalidData && !invalidDataFound) {
323 invalidDataFound = true;
324 }
325 }
326 if (invalidDataFound) {
327 // Prepare the TimeSeriesProperty<bool>
328 // It's name will be the name of the property plus suffix "_invalid_values"
329 const auto tspName = API::LogManager::getInvalidValuesFilterLogName(prop.name());
330 auto tsp = std::make_unique<TimeSeriesProperty<bool>>(tspName);
331 tsp->create(times, boolValues);
332 log.debug() << " done reading \"value_valid\" array\n";
333
334 return tsp;
335 } else {
336 // no data found
337 return std::unique_ptr<Kernel::Property>(nullptr);
338 }
339}
340
355void appendEndTimeLog(Kernel::Property *prop, const API::Run &run) {
356 // do not modify proton charge
357 if (prop->name() == "proton_charge")
358 return;
359
360 try {
361 auto tsLog = dynamic_cast<TimeSeriesProperty<double> *>(prop);
362 const auto endTime = run.endTime();
363
364 // First check if it is valid to append a log entry
365 if (!tsLog || tsLog->size() == 0 || endTime <= tsLog->lastTime())
366 return;
367
368 tsLog->addValue(endTime, tsLog->lastValue());
369 } catch (const Exception::NotFoundError &) {
370 // pass
371 } catch (const std::runtime_error &) {
372 // pass
373 }
374}
375
382void readStartAndEndTime(Nexus::File &file, API::Run &run) {
383 try {
384 // Read the start and end time strings
385 file.openData("start_time");
386 Types::Core::DateAndTime start(file.getStrData());
387 file.closeData();
388 file.openData("end_time");
389 Types::Core::DateAndTime end(file.getStrData());
390 file.closeData();
391 run.setStartAndEndTime(start, end);
392 } catch (Nexus::Exception const &) {
393 }
394}
395
396} // End of anonymous namespace
397
400
403 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("Workspace", "Anonymous", Direction::InOut),
404 "The name of the workspace that will be filled with the logs.");
405 const std::vector<std::string> exts{".nxs", ".n*"};
406 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts),
407 "Path to the .nxs file to load. Can be an EventNeXus or a "
408 "histogrammed NeXus.");
409 declareProperty(std::make_unique<PropertyWithValue<bool>>("OverwriteLogs", true, Direction::Input),
410 "If true then some existing logs will be overwritten, if false they will "
411 "not.");
412 declareProperty(std::make_unique<PropertyWithValue<std::string>>("NXentryName", "", Direction::Input),
413 "Entry in the nexus file from which to read the logs");
414 declareProperty(std::make_unique<PropertyWithValue<std::vector<std::string>>>("AllowList", std::vector<std::string>(),
416 "If specified, only these logs will be loaded from the file (each "
417 "separated by a comma).");
418 declareProperty(std::make_unique<PropertyWithValue<std::vector<std::string>>>("BlockList", std::vector<std::string>(),
420 "If specified, logs matching one of the patterns will NOT be loaded from the file (each "
421 "separated by a comma).");
422}
423
432 std::string filename = getPropertyValue("Filename");
434
435 std::string entry_name = getPropertyValue("NXentryName");
436
437 std::vector<std::string> allow_list = getProperty("AllowList");
438 std::vector<std::string> block_list = getProperty("BlockList");
439
440 // Find the entry name to use (normally "entry" for SNS, "raw_data_1" for
441 // ISIS) if entry name is empty
442 if (entry_name.empty()) {
443 entry_name = LoadTOFRawNexus::getEntryName(filename);
444 }
445 Nexus::File file(filename);
446 // Find the root entry
447 try {
448 file.openGroup(entry_name, "NXentry");
449 } catch (Nexus::Exception const &) {
450 throw std::invalid_argument("Unknown NeXus file format found in file '" + filename + "', or '" + entry_name +
451 "' is not a valid NXentry");
452 }
453
456 try {
457 file.openAddress("DASlogs");
458 try {
459 file.openGroup("frequency", "NXlog");
460 try {
461 file.openData("time");
462
463 //----- Start time is an ISO8601 string date and time. ------
464 try {
465 file.getAttr("start", freqStart);
466
467 } catch (Nexus::Exception const &) {
468 // Some logs have "offset" instead of start
469 try {
470 file.getAttr("offset", freqStart);
471 } catch (Nexus::Exception const &) {
472 g_log.warning() << "Log entry has no start time indicated.\n";
473 file.closeData();
474 throw;
475 }
476 }
477 file.closeData();
478 } catch (Nexus::Exception const &) {
479 // No time. This is not an SNS SNAP file
480 }
481 file.closeGroup();
482 } catch (Nexus::Exception const &) {
483 // No time. This is not an SNS frequency group
484 }
485 file.closeGroup();
486 } catch (Nexus::Exception const &) {
487 // No time. This is not an SNS group
488 }
489
490 readStartAndEndTime(file, workspace->mutableRun());
491
492 if (!allow_list.empty() && !block_list.empty()) {
493 throw std::runtime_error("BlockList and AllowList are mutually exclusive! "
494 "Please only enter values for one of these fields.");
495 }
496
497 const std::map<std::string, std::set<std::string>> &allEntries = getFileInfo()->getAllEntries();
498
499 auto lf_LoadLogsByClass = [&](const std::string &group_class, const bool isLog) {
500 auto itGroupClass = allEntries.find(group_class);
501 if (itGroupClass == allEntries.end()) {
502 return;
503 }
504 const std::set<std::string> &entries = itGroupClass->second;
505 // still a linear search
506 for (const std::string &entry : entries) {
507 // match for 2nd level entry /a/b
508 if (std::count(entry.begin(), entry.end(), '/') == 2) {
509 if (isLog) {
510 loadLogs(file, entry, group_class, workspace, allow_list, block_list);
511 } else {
512 loadNPeriods(file, workspace);
513 }
514 }
515 }
516 };
517 lf_LoadLogsByClass("IXselog", true);
518 lf_LoadLogsByClass("IXrunlog", true);
519 lf_LoadLogsByClass("IXperiods", false);
520
521 auto lf_LoadLogsByName = [&](const std::string &group_name) {
522 for (auto itGroupClass = allEntries.begin(); itGroupClass != allEntries.end(); ++itGroupClass) {
523
524 const std::string &group_class = itGroupClass->first;
525 const std::set<std::string> &entries = itGroupClass->second;
526
527 const std::string absoluteGroupName = "/" + entry_name + "/" + group_name;
528 auto itGroupName = entries.find(absoluteGroupName);
529 if (itGroupName == entries.end()) {
530 continue;
531 }
532 // here we must search only in NxLogs and NXpositioner sets
533 loadLogs(file, absoluteGroupName, group_class, workspace, allow_list, block_list);
534 }
535 };
536
537 lf_LoadLogsByName("DASlogs");
538 lf_LoadLogsByName("framelog");
539
540 // If there's measurement information, load that info as logs.
541 loadAndApplyMeasurementInfo(&file, *workspace);
542 // If there's title information, load that info as logs.
543 loadAndApplyRunTitle(&file, *workspace);
544
545 // Freddie Akeroyd 12/10/2011
546 // current ISIS implementation contains an additional indirection between
547 // collected frames via an
548 // "event_frame_number" array in NXevent_data (which eliminates frames with no
549 // events).
550 // the proton_log is for all frames and so is longer than the event_index
551 // array, so we need to
552 // filter the proton_charge log based on event_frame_number
553 // This difference will be removed in future for compatibility with SNS, but
554 // the code below will allow current SANS2D files to load
555 if (workspace->mutableRun().hasProperty("proton_log")) {
556 std::vector<int> event_frame_number;
557 this->getLogger().notice() << "Using old ISIS proton_log and event_frame_number indirection...\n";
558 try {
559 // Find the bank/name corresponding to the first event data entry, i.e.
560 // one with type NXevent_data.
561 file.openAddress("/" + entry_name);
562 auto itEventData = allEntries.find("NXevent_data");
563 if (itEventData != allEntries.end()) {
564 const std::set<std::string> &events = itEventData->second;
565 for (const std::string &event : events) {
566 const std::string eventEntry = event.substr(event.find_last_of("/") + 1);
567
568 this->getLogger().debug() << "Opening"
569 << " /" + entry_name + "/" + eventEntry + "/event_frame_number"
570 << " to find the event_frame_number\n";
571 file.openAddress("/" + entry_name + "/" + eventEntry + "/event_frame_number");
572 file.getData(event_frame_number);
573 }
574 }
575 } catch (Nexus::Exception const &) {
576 this->getLogger().warning() << "Unable to load event_frame_number - "
577 "filtering events by time will not work \n";
578 }
579 file.openAddress("/" + entry_name);
580 if (!event_frame_number.empty()) // ISIS indirection - see above comments
581 {
583 dynamic_cast<Kernel::TimeSeriesProperty<double> *>(workspace->mutableRun().getProperty("proton_log"));
584 if (!plog)
585 throw std::runtime_error("Could not cast (interpret) proton_log as a time "
586 "series property. Cannot continue.");
588 std::vector<double> pval;
589 std::vector<Mantid::Types::Core::DateAndTime> ptime;
590 pval.reserve(event_frame_number.size());
591 ptime.reserve(event_frame_number.size());
592 std::vector<Mantid::Types::Core::DateAndTime> plogt = plog->timesAsVector();
593 std::vector<double> plogv = plog->valuesAsVector();
594 for (auto number : event_frame_number) {
595 ptime.emplace_back(plogt[number]);
596 pval.emplace_back(plogv[number]);
597 }
598 pcharge->create(ptime, pval);
599 pcharge->setUnits("uAh");
600 workspace->mutableRun().addProperty(pcharge, true);
601 }
602 }
603
604 if (!workspace->run().hasProperty(workspace->run().getProtonChargeLogName())) {
605 try {
606 // Try pulling proton charge from the main proton_charge entry
607 file.openData("proton_charge");
608 std::vector<double> values;
609 file.getDataCoerce(values);
610 std::string units;
611 file.getAttr("units", units);
612 double charge = values.front();
613 if (units.find("picoCoulomb") != std::string::npos) {
614 charge *= 1.e-06 / 3600.;
615 }
616 workspace->mutableRun().setProtonCharge(charge);
617 } catch (Nexus::Exception const &) {
618 // Try and integrate the proton logs
619 try {
620 // Use the DAS logs to integrate the proton charge (if any).
621 workspace->mutableRun().getProtonCharge();
622 } catch (Exception::NotFoundError &) {
623 // Ignore not found property error.
624 }
625 }
626 }
627 // For period data mark proton charge log value as unfiltered to enable subsequent filtering by period.
628 if (workspace->run().hasProperty("proton_charge_by_period")) {
629 Kernel::PropertyWithValue<int> *pChargeUnfiltered =
630 new Kernel::PropertyWithValue<int>(workspace->run().getProtonChargeUnfilteredName(), 1);
631 workspace->mutableRun().addProperty(pChargeUnfiltered, true);
632 }
633
634 if (!allow_list.empty()) {
635 for (const auto &allow : allow_list) {
636 if (!workspace->run().hasProperty(allow)) {
637 g_log.notice() << "could not load entry '" << allow << "' that was specified in the allow list"
638 << "\n";
639 }
640 }
641 }
642
643 // Close the file
644 file.close();
645
646 if (m_logsWithInvalidValues.size() > 0) {
647 if (m_logsWithInvalidValues.size() == 1) {
648 g_log.warning() << "Sample Log \"" << m_logsWithInvalidValues[0]
649 << "\" contains invalid values, click \"Show Sample Logs\" "
650 "for details.\n";
651 }
652 auto other_string = (m_logsWithInvalidValues.size() < 2) ? " other " : " others";
653 g_log.warning() << "Sample Log \"" << m_logsWithInvalidValues[0] << "\" and " << m_logsWithInvalidValues.size() - 1
654 << other_string
655 << " contain invalid values, click \"Show Sample Logs\" for "
656 "details.\n";
657 }
658}
659
666void LoadNexusLogs::loadVetoPulses(Nexus::File &file, const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
667 try {
668 file.openGroup("Veto_pulse", "NXgroup");
669 } catch (Nexus::Exception const &) {
670 // No group. This is common in older files
671 return;
672 }
673 file.openData("veto_pulse_time");
674
675 // Load the start date/time as ISO8601 string.
676 std::string start_time;
677 file.getAttr("start_time", start_time);
678 DateAndTime start(start_time);
679
680 // Read the offsets
681 std::vector<double> time_double;
682 file.getData(time_double);
683
684 // Fake values with zeroes.
685 std::vector<double> values(time_double.size(), 0.0);
686 TimeSeriesProperty<double> *tsp = new TimeSeriesProperty<double>("veto_pulse_time");
687 tsp->create(start, time_double, values);
688 tsp->setUnits("");
689
690 // Add the log
691 workspace->mutableRun().addProperty(tsp);
692
693 file.closeData();
694 file.closeGroup();
695}
696
705void LoadNexusLogs::loadNPeriods(Nexus::File &file, const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
706 int value = 1; // Default to 1-period unless
707 try {
708 file.openGroup("periods", "IXperiods");
709 file.openData("number");
710 file.getData(&value);
711 file.closeData();
712 file.closeGroup();
713 } catch (Nexus::Exception const &) {
714 // Likely missing IXperiods.
715 return;
716 }
717
718 API::Run &run = workspace->mutableRun();
719 const std::string nPeriodsLabel = "nperiods";
720 if (!run.hasProperty(nPeriodsLabel)) {
721 run.addProperty(new PropertyWithValue<int>(nPeriodsLabel, value));
722 }
723
724 // For ISIS Nexus only, fabricate an additional log containing an array of
725 // proton charge information from the periods group.
726 try {
727 file.openGroup("periods", "IXperiods");
728
729 // Get the number of periods again
730 file.openData("number");
731 int numberOfPeriods = 0;
732 file.getData(&numberOfPeriods);
733 file.closeData();
734
735 // Get the proton charge vector
736 std::vector<double> protonChargeByPeriod(numberOfPeriods);
737 file.openData("proton_charge");
738 file.getDataCoerce(protonChargeByPeriod);
739 file.closeData();
740
741 // Add the proton charge vector
742 const std::string protonChargeByPeriodLabel = "proton_charge_by_period";
743 if (!run.hasProperty(protonChargeByPeriodLabel)) {
744 run.addProperty(new ArrayProperty<double>(protonChargeByPeriodLabel, std::move(protonChargeByPeriod)));
745 }
746 file.closeGroup();
747 } catch (Nexus::Exception const &) {
748 this->g_log.debug("Cannot read periods information from the nexus file. "
749 "This group may be absent.");
750 file.closeGroup();
751 } catch (std::runtime_error &) {
752 this->g_log.debug("Cannot read periods information from the nexus file. "
753 "This group may be absent.");
754 file.closeGroup();
755 }
756}
757
768void LoadNexusLogs::loadLogs(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class,
769 const std::shared_ptr<API::MatrixWorkspace> &workspace,
770 const std::vector<std::string> &allow_list,
771 const std::vector<std::string> &block_list) const {
772
773 const std::map<std::string, std::set<std::string>> &allEntries = getFileInfo()->getAllEntries();
774
775 auto lf_LoadByLogClass = [&](const std::string &logClass, const bool isNxLog) {
776 auto itLogClass = allEntries.find(logClass);
777 if (itLogClass == allEntries.end()) {
778 return;
779 }
780 const std::set<std::string> &logsSet = itLogClass->second;
781 auto itPrefixBegin = logsSet.lower_bound(absolute_entry_name);
782
783 if (allow_list.empty()) {
784 // convert the blocklist into a bunch of objects to handle globbing
785 const bool has_block_list = (!block_list.empty());
786 std::vector<std::unique_ptr<Poco::Glob>> globblock_list;
787 if (has_block_list) {
788 std::transform(block_list.cbegin(), block_list.cend(), std::back_inserter(globblock_list),
789 [](const auto &block) { return std::make_unique<Poco::Glob>(block); });
790 }
791
792 for (auto it = itPrefixBegin;
793 it != logsSet.end() && it->compare(0, absolute_entry_name.size(), absolute_entry_name) == 0; ++it) {
794 // must be third level entry
795 if (std::count(it->begin(), it->end(), '/') == 3) {
796 if (has_block_list) {
797 bool skip = false;
798 for (auto &block : globblock_list) {
799 if (block->match((*it).substr((*it).find_last_of("/") + 1))) {
800 skip = true;
801 break; // from the loop of block items
802 }
803 }
804 if (skip) {
805 continue; // go to next log
806 }
807 } // end of looping over block_list
808
809 if (isNxLog) {
810 loadNXLog(file, *it, logClass, workspace);
811 } else {
812 loadSELog(file, *it, workspace);
813 }
814 }
815 }
816 } else {
817 for (const auto &allow : allow_list) {
818 itPrefixBegin = logsSet.find(absolute_entry_name + "/" + allow);
819 if (itPrefixBegin == logsSet.end()) {
820 // don't print warning yet since it might be found in another log
821 // class
822 continue;
823 }
824 auto it = itPrefixBegin;
825 // must be third level entry
826 if (std::count(it->begin(), it->end(), '/') == 3) {
827 if (isNxLog) {
828 loadNXLog(file, *it, logClass, workspace);
829 } else {
830 loadSELog(file, *it, workspace);
831 }
832 }
833 }
834 }
835 };
836
837 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
838 file.openGroup(entry_name, entry_class);
839 lf_LoadByLogClass("NXlog", true);
840 lf_LoadByLogClass("NXpositioner", true);
841 lf_LoadByLogClass("IXseblock", false);
843
844 file.closeGroup();
845}
846
855void LoadNexusLogs::loadNXLog(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class,
856 const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
857
858 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
859 g_log.debug() << "processing " << entry_name << ":" << entry_class << "\n";
860 file.openGroup(entry_name, entry_class);
861 // Validate the NX log class.
862 // Just verify that time and value entries exist
863 const std::string timeEntry = absolute_entry_name + "/time";
864 const std::string valueEntry = absolute_entry_name + "/value";
865 const std::string validatorEntry = absolute_entry_name + "/value_valid";
866 bool foundValue = false;
867 bool foundTime = false;
868 bool foundValidator = false;
869
870 const std::map<std::string, std::set<std::string>> &allEntries = getFileInfo()->getAllEntries();
871 // reverse search to take advantage of the fact that these are located in SDS
872 for (auto it = allEntries.rbegin(); it != allEntries.rend(); ++it) {
873 const std::set<std::string> &entriesSet = it->second;
874 if (entriesSet.count(timeEntry) == 1) {
875 foundTime = true;
876 }
877 if (entriesSet.count(valueEntry) == 1) {
878 foundValue = true;
879 }
880 if (entriesSet.count(validatorEntry) == 1) {
881 foundValidator = true;
882 }
883 if (foundTime && foundValue && foundValidator) {
884 break;
885 }
886 }
887
888 if (!foundTime || !foundValue) {
889 g_log.warning() << "Invalid NXlog entry " << entry_name << " found. Did not contain 'value' and 'time'.\n";
890 file.closeGroup();
891 return;
892 }
893
894 // whether to overwrite logs on workspace
895 bool overwritelogs = this->getProperty("OverwriteLogs");
896 try {
897 if (overwritelogs || !(workspace->run().hasProperty(entry_name))) {
898 auto logValue = createTimeSeries(file, entry_name, freqStart, g_log);
899 // Create (possibly) a boolean time series, companion to time series `entry_name`
900 if (foundValidator) {
901 auto validityLogValue = createTimeSeriesValidityFilter(file, *logValue, g_log);
902 if (validityLogValue) {
903 appendEndTimeLog(validityLogValue.get(), workspace->run());
904 workspace->mutableRun().addProperty(std::move(validityLogValue), overwritelogs);
905 m_logsWithInvalidValues.emplace_back(entry_name);
906 }
907 }
908 appendEndTimeLog(logValue.get(), workspace->run());
909 workspace->mutableRun().addProperty(std::move(logValue), overwritelogs);
910 }
911 } catch (Nexus::Exception const &e) {
912 g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading:'" << e.what() << "'.\n";
913 } catch (std::invalid_argument &e) {
914 g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading:'" << e.what() << "'.\n";
915 }
916
917 file.closeGroup();
918}
919
920void LoadNexusLogs::loadSELog(Nexus::File &file, const std::string &absolute_entry_name,
921 const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
922 // Open the entry
923 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
924
925 file.openGroup(entry_name, "IXseblock");
926 std::string propName = entry_name;
927 if (workspace->run().hasProperty(propName)) {
928 propName = "selog_" + propName;
929 }
930 // There are two possible entries:
931 // value_log - A time series entry. This can contain a corrupt value entry
932 // so if it does use the value one
933 // value - A single value float entry
934 const std::string valueEntry = absolute_entry_name + "/value";
935 const std::string valueLogEntry = absolute_entry_name + "/value_log";
936 bool foundValue = false;
937 bool foundValueLog = false;
938
939 const std::map<std::string, std::set<std::string>> &allEntries = getFileInfo()->getAllEntries();
940
941 for (auto it = allEntries.rbegin(); it != allEntries.rend(); ++it) {
942 const std::set<std::string> &entriesSet = it->second;
943 if (entriesSet.count(valueEntry) == 1) {
944 foundValue = true;
945 }
946 if (entriesSet.count(valueLogEntry) == 1) {
947 foundValueLog = true;
948 // takes precedence
949 break;
950 }
951 }
952
953 std::unique_ptr<Kernel::Property> logValue;
954 if (foundValueLog) {
955 try {
956 try {
957 file.openGroup("value_log", "NXlog");
958 } catch (Nexus::Exception const &) {
959 file.closeGroup();
960 throw;
961 }
962
963 logValue = createTimeSeries(file, propName, freqStart, g_log);
964 // Create (possibly) a boolean time series, companion to time series `logValue`.
965 auto validityLogValue = createTimeSeriesValidityFilter(file, *logValue, g_log);
966 if (validityLogValue) {
967 appendEndTimeLog(validityLogValue.get(), workspace->run());
968 workspace->mutableRun().addProperty(std::move(validityLogValue));
969 m_logsWithInvalidValues.emplace_back(propName);
970 }
971 appendEndTimeLog(logValue.get(), workspace->run());
972
973 file.closeGroup();
974 } catch (std::exception &e) {
975 g_log.warning() << "IXseblock entry '" << entry_name << "' gave an error when loading "
976 << "a time series:'" << e.what() << "'. Skipping entry\n";
977 file.closeGroup(); // value_log
978 file.closeGroup(); // entry_name
979 return;
980 }
981 } else if (foundValue) {
982 try {
983 // This may have a larger dimension than 1 bit it has no time field so
984 // take the first entry
985 file.openData("value");
986 Nexus::Info info = file.getInfo();
987 if (info.type == NXnumtype::FLOAT32) {
988 boost::scoped_array<float> value(new float[info.dims[0]]);
989 file.getData(value.get());
990 file.closeData();
991 logValue = std::make_unique<Kernel::PropertyWithValue<double>>(propName, static_cast<double>(value[0]), true);
992 } else {
993 file.closeGroup();
994 return;
995 }
996 } catch (Nexus::Exception const &e) {
997 g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading "
998 << "a single value:'" << e.what() << "'.\n";
999 file.closeData();
1000 file.closeGroup();
1001 return;
1002 }
1003 } else {
1004 g_log.warning() << "IXseblock entry " << entry_name << " cannot be read, skipping entry.\n";
1005 file.closeGroup();
1006 return;
1007 }
1008 workspace->mutableRun().addProperty(std::move(logValue));
1009 file.closeGroup();
1010}
1011
1012} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
IPeaksWorkspace_sptr workspace
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.
Kernel::Logger & getLogger() const
Returns a reference to the logger.
@ Load
allowed here which will be passed to the algorithm
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
static std::string getInvalidValuesFilterLogName(const std::string &logName)
Gets the correct log name for the matching invalid values log for a given log name.
virtual const std::shared_ptr< Mantid::Nexus::NexusDescriptor > getFileInfo() const noexcept
Required to pass m_fileInfo to static functions Keeping it shared_ptr to match setFileInfo signature ...
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
A property class for workspaces.
std::vector< std::string > m_logsWithInvalidValues
void init() override
Overwrites Algorithm method.
LoadNexusLogs()
Default constructor.
void execLoader() override
Overwrites Algorithm method.
void loadNXLog(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class, const std::shared_ptr< API::MatrixWorkspace > &workspace) const
Load an NXlog entry.
void loadNPeriods(Nexus::File &file, const std::shared_ptr< API::MatrixWorkspace > &workspace) const
For ISIS logs containing periods, retrieve the total proton charge for each period if stored in the l...
std::string freqStart
Use frequency start for Monitor19 and Special1_19 logs with "No Time" for SNAP.
void loadSELog(Nexus::File &file, const std::string &absolute_entry_name, const std::shared_ptr< API::MatrixWorkspace > &workspace) const
Load an IXseblock entry.
void loadVetoPulses(Nexus::File &file, const std::shared_ptr< API::MatrixWorkspace > &workspace) const
Try to load the "Veto_pulse" field in DASLogs and convert it to a sample log.
void loadLogs(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class, const std::shared_ptr< API::MatrixWorkspace > &workspace, const std::vector< std::string > &allow_list, const std::vector< std::string > &block_list) const
Load log data from a group.
static std::string getEntryName(const std::string &filename)
Support for a property that holds an array of values.
Exception for when an item is not found in a collection.
Definition Exception.h:145
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
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
The concrete, templated class for properties.
virtual void setUnits(const std::string &unit)
Sets the units of the property, as a string.
Definition Property.cpp:198
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>
void create(const Types::Core::DateAndTime &start_time, const std::vector< double > &time_sec, const std::vector< TYPE > &new_values)
Clears and creates a TimeSeriesProperty from these parameters.
std::vector< Types::Core::DateAndTime > timesAsVector() const override
Return the time series's times as a vector<DateAndTime>
Class that provides for a standard Nexus exception.
static unsigned short constexpr CHAR
static unsigned short constexpr FLOAT32
static unsigned short constexpr FLOAT64
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53
This structure holds the type and dimensions of a primative field/array.
DimVector dims
The dimensions of the file.
NXnumtype type
The primative type for the field.