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 auto lf_LoadLogsByClass = [&](const std::string &group_class, const bool isLog) {
498 const std::set<std::string> &entries = file.getEntriesByClass(group_class);
499 // still a linear search
500 for (const std::string &entry : entries) {
501 // match for 2nd level entry /a/b
502 if (std::count(entry.begin(), entry.end(), '/') == 2) {
503 if (isLog) {
504 loadLogs(file, entry, group_class, workspace, allow_list, block_list);
505 } else {
506 loadNPeriods(file, workspace);
507 }
508 }
509 }
510 };
511 lf_LoadLogsByClass("IXselog", true);
512 lf_LoadLogsByClass("IXrunlog", true);
513 lf_LoadLogsByClass("IXperiods", false);
514
515 auto lf_LoadLogsByName = [&](const std::string &group_name) {
516 std::string const absoluteGroupName = "/" + entry_name + "/" + group_name;
517 if (file.hasAddress(absoluteGroupName)) {
518 std::string const &group_class = file.classForEntry(absoluteGroupName);
519 loadLogs(file, absoluteGroupName, group_class, workspace, allow_list, block_list);
520 }
521 };
522
523 lf_LoadLogsByName("DASlogs");
524 lf_LoadLogsByName("framelog");
525
526 // If there's measurement information, load that info as logs.
527 loadAndApplyMeasurementInfo(&file, *workspace);
528 // If there's title information, load that info as logs.
529 loadAndApplyRunTitle(&file, *workspace);
530
531 // Freddie Akeroyd 12/10/2011
532 // current ISIS implementation contains an additional indirection between
533 // collected frames via an
534 // "event_frame_number" array in NXevent_data (which eliminates frames with no
535 // events).
536 // the proton_log is for all frames and so is longer than the event_index
537 // array, so we need to
538 // filter the proton_charge log based on event_frame_number
539 // This difference will be removed in future for compatibility with SNS, but
540 // the code below will allow current SANS2D files to load
541 if (workspace->mutableRun().hasProperty("proton_log")) {
542 std::vector<int> event_frame_number;
543 this->getLogger().notice() << "Using old ISIS proton_log and event_frame_number indirection...\n";
544 try {
545 // Find the bank/name corresponding to the first event data entry, i.e.
546 // one with type NXevent_data.
547 file.openAddress("/" + entry_name);
548 std::set<std::string> const &events = file.getEntriesByClass("NXevent_data");
549 for (std::string const &event : events) {
550 std::string const eventEntry = event.substr(event.find_last_of("/") + 1);
551
552 this->getLogger().debug() << "Opening"
553 << " /" + entry_name + "/" + eventEntry + "/event_frame_number"
554 << " to find the event_frame_number\n";
555 file.openAddress("/" + entry_name + "/" + eventEntry + "/event_frame_number");
556 file.getData(event_frame_number);
557 }
558 } catch (Nexus::Exception const &) {
559 this->getLogger().warning() << "Unable to load event_frame_number - "
560 "filtering events by time will not work \n";
561 }
562 file.openAddress("/" + entry_name);
563 if (!event_frame_number.empty()) // ISIS indirection - see above comments
564 {
566 dynamic_cast<Kernel::TimeSeriesProperty<double> *>(workspace->mutableRun().getProperty("proton_log"));
567 if (!plog)
568 throw std::runtime_error("Could not cast (interpret) proton_log as a time "
569 "series property. Cannot continue.");
571 std::vector<double> pval;
572 std::vector<Mantid::Types::Core::DateAndTime> ptime;
573 pval.reserve(event_frame_number.size());
574 ptime.reserve(event_frame_number.size());
575 std::vector<Mantid::Types::Core::DateAndTime> plogt = plog->timesAsVector();
576 std::vector<double> plogv = plog->valuesAsVector();
577 for (auto number : event_frame_number) {
578 ptime.emplace_back(plogt[number]);
579 pval.emplace_back(plogv[number]);
580 }
581 pcharge->create(ptime, pval);
582 pcharge->setUnits("uAh");
583 workspace->mutableRun().addProperty(pcharge, true);
584 }
585 }
586
587 if (!workspace->run().hasProperty(workspace->run().getProtonChargeLogName())) {
588 try {
589 // Try pulling proton charge from the main proton_charge entry
590 file.openData("proton_charge");
591 std::vector<double> values;
592 file.getDataCoerce(values);
593 std::string units;
594 file.getAttr("units", units);
595 double charge = values.front();
596 if (units.find("picoCoulomb") != std::string::npos) {
597 charge *= 1.e-06 / 3600.;
598 }
599 workspace->mutableRun().setProtonCharge(charge);
600 } catch (Nexus::Exception const &) {
601 // Try and integrate the proton logs
602 try {
603 // Use the DAS logs to integrate the proton charge (if any).
604 workspace->mutableRun().getProtonCharge();
605 } catch (Exception::NotFoundError &) {
606 // Ignore not found property error.
607 }
608 }
609 }
610 // For period data mark proton charge log value as unfiltered to enable subsequent filtering by period.
611 if (workspace->run().hasProperty("proton_charge_by_period")) {
612 Kernel::PropertyWithValue<int> *pChargeUnfiltered =
613 new Kernel::PropertyWithValue<int>(workspace->run().getProtonChargeUnfilteredName(), 1);
614 workspace->mutableRun().addProperty(pChargeUnfiltered, true);
615 }
616
617 if (!allow_list.empty()) {
618 for (const auto &allow : allow_list) {
619 if (!workspace->run().hasProperty(allow)) {
620 g_log.notice() << "could not load entry '" << allow << "' that was specified in the allow list"
621 << "\n";
622 }
623 }
624 }
625
626 // Close the file
627 file.close();
628
629 if (m_logsWithInvalidValues.size() > 0) {
630 if (m_logsWithInvalidValues.size() == 1) {
631 g_log.warning() << "Sample Log \"" << m_logsWithInvalidValues[0]
632 << "\" contains invalid values, click \"Show Sample Logs\" "
633 "for details.\n";
634 }
635 auto other_string = (m_logsWithInvalidValues.size() < 2) ? " other " : " others";
636 g_log.warning() << "Sample Log \"" << m_logsWithInvalidValues[0] << "\" and " << m_logsWithInvalidValues.size() - 1
637 << other_string
638 << " contain invalid values, click \"Show Sample Logs\" for "
639 "details.\n";
640 }
641}
642
649void LoadNexusLogs::loadVetoPulses(Nexus::File &file, const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
650 try {
651 file.openGroup("Veto_pulse", "NXgroup");
652 } catch (Nexus::Exception const &) {
653 // No group. This is common in older files
654 return;
655 }
656 file.openData("veto_pulse_time");
657
658 // Load the start date/time as ISO8601 string.
659 std::string start_time;
660 file.getAttr("start_time", start_time);
661 DateAndTime start(start_time);
662
663 // Read the offsets
664 std::vector<double> time_double;
665 file.getData(time_double);
666
667 // Fake values with zeroes.
668 std::vector<double> values(time_double.size(), 0.0);
669 TimeSeriesProperty<double> *tsp = new TimeSeriesProperty<double>("veto_pulse_time");
670 tsp->create(start, time_double, values);
671 tsp->setUnits("");
672
673 // Add the log
674 workspace->mutableRun().addProperty(tsp);
675
676 file.closeData();
677 file.closeGroup();
678}
679
688void LoadNexusLogs::loadNPeriods(Nexus::File &file, const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
689 int value = 1; // Default to 1-period unless
690 try {
691 file.openGroup("periods", "IXperiods");
692 file.openData("number");
693 file.getData(&value);
694 file.closeData();
695 file.closeGroup();
696 } catch (Nexus::Exception const &) {
697 // Likely missing IXperiods.
698 return;
699 }
700
701 API::Run &run = workspace->mutableRun();
702 const std::string nPeriodsLabel = "nperiods";
703 if (!run.hasProperty(nPeriodsLabel)) {
704 run.addProperty(new PropertyWithValue<int>(nPeriodsLabel, value));
705 }
706
707 // For ISIS Nexus only, fabricate an additional log containing an array of
708 // proton charge information from the periods group.
709 try {
710 file.openGroup("periods", "IXperiods");
711
712 // Get the number of periods again
713 file.openData("number");
714 int numberOfPeriods = 0;
715 file.getData(&numberOfPeriods);
716 file.closeData();
717
718 // Get the proton charge vector
719 std::vector<double> protonChargeByPeriod(numberOfPeriods);
720 file.openData("proton_charge");
721 file.getDataCoerce(protonChargeByPeriod);
722 file.closeData();
723
724 // Add the proton charge vector
725 const std::string protonChargeByPeriodLabel = "proton_charge_by_period";
726 if (!run.hasProperty(protonChargeByPeriodLabel)) {
727 run.addProperty(new ArrayProperty<double>(protonChargeByPeriodLabel, std::move(protonChargeByPeriod)));
728 }
729 file.closeGroup();
730 } catch (Nexus::Exception const &) {
731 this->g_log.debug("Cannot read periods information from the nexus file. "
732 "This group may be absent.");
733 file.closeGroup();
734 } catch (std::runtime_error &) {
735 this->g_log.debug("Cannot read periods information from the nexus file. "
736 "This group may be absent.");
737 file.closeGroup();
738 }
739}
740
751void LoadNexusLogs::loadLogs(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class,
752 const std::shared_ptr<API::MatrixWorkspace> &workspace,
753 const std::vector<std::string> &allow_list,
754 const std::vector<std::string> &block_list) const {
755
756 auto lf_LoadByLogClass = [&](std::string const &logClass, const bool isNxLog) {
757 std::set<std::string> const &logsSet = file.getEntriesByClass(logClass);
758 if (logsSet.empty()) {
759 return;
760 }
761 auto itPrefixBegin = logsSet.lower_bound(absolute_entry_name);
762
763 if (allow_list.empty()) {
764 // convert the blocklist into a bunch of objects to handle globbing
765 const bool has_block_list = (!block_list.empty());
766 std::vector<std::unique_ptr<Poco::Glob>> globblock_list;
767 if (has_block_list) {
768 std::transform(block_list.cbegin(), block_list.cend(), std::back_inserter(globblock_list),
769 [](const auto &block) { return std::make_unique<Poco::Glob>(block); });
770 }
771
772 for (auto it = itPrefixBegin;
773 it != logsSet.end() && it->compare(0, absolute_entry_name.size(), absolute_entry_name) == 0; ++it) {
774 // must be third level entry
775 if (std::count(it->begin(), it->end(), '/') == 3) {
776 if (has_block_list) {
777 bool skip = false;
778 for (auto &block : globblock_list) {
779 if (block->match((*it).substr((*it).find_last_of("/") + 1))) {
780 skip = true;
781 break; // from the loop of block items
782 }
783 }
784 if (skip) {
785 continue; // go to next log
786 }
787 } // end of looping over block_list
788
789 if (isNxLog) {
790 loadNXLog(file, *it, logClass, workspace);
791 } else {
792 loadSELog(file, *it, workspace);
793 }
794 }
795 }
796 } else {
797 for (const auto &allow : allow_list) {
798 itPrefixBegin = logsSet.find(absolute_entry_name + "/" + allow);
799 if (itPrefixBegin == logsSet.end()) {
800 // don't print warning yet since it might be found in another log
801 // class
802 continue;
803 }
804 auto it = itPrefixBegin;
805 // must be third level entry
806 if (std::count(it->begin(), it->end(), '/') == 3) {
807 if (isNxLog) {
808 loadNXLog(file, *it, logClass, workspace);
809 } else {
810 loadSELog(file, *it, workspace);
811 }
812 }
813 }
814 }
815 };
816
817 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
818 file.openGroup(entry_name, entry_class);
819 lf_LoadByLogClass("NXlog", true);
820 lf_LoadByLogClass("NXpositioner", true);
821 lf_LoadByLogClass("IXseblock", false);
823
824 file.closeGroup();
825}
826
835void LoadNexusLogs::loadNXLog(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class,
836 const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
837
838 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
839 g_log.debug() << "processing " << entry_name << ":" << entry_class << "\n";
840 file.openGroup(entry_name, entry_class);
841 // Validate the NX log class.
842 // Just verify that time and value entries exist
843 std::string const timeEntry = absolute_entry_name + "/time";
844 std::string const valueEntry = absolute_entry_name + "/value";
845 std::string const validatorEntry = absolute_entry_name + "/value_valid";
846 bool const foundValue = file.hasAddress(valueEntry);
847 bool const foundTime = file.hasAddress(timeEntry);
848 bool const foundValidator = file.hasAddress(validatorEntry);
849
850 if (!foundTime || !foundValue) {
851 g_log.warning() << "Invalid NXlog entry " << entry_name << " found. Did not contain 'value' and 'time'.\n";
852 file.closeGroup();
853 return;
854 }
855
856 // whether to overwrite logs on workspace
857 bool overwritelogs = this->getProperty("OverwriteLogs");
858 try {
859 if (overwritelogs || !(workspace->run().hasProperty(entry_name))) {
860 auto logValue = createTimeSeries(file, entry_name, freqStart, g_log);
861 // Create (possibly) a boolean time series, companion to time series `entry_name`
862 if (foundValidator) {
863 auto validityLogValue = createTimeSeriesValidityFilter(file, *logValue, g_log);
864 if (validityLogValue) {
865 appendEndTimeLog(validityLogValue.get(), workspace->run());
866 workspace->mutableRun().addProperty(std::move(validityLogValue), overwritelogs);
867 m_logsWithInvalidValues.emplace_back(entry_name);
868 }
869 }
870 appendEndTimeLog(logValue.get(), workspace->run());
871 workspace->mutableRun().addProperty(std::move(logValue), overwritelogs);
872 }
873 } catch (Nexus::Exception const &e) {
874 g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading:'" << e.what() << "'.\n";
875 } catch (std::invalid_argument &e) {
876 g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading:'" << e.what() << "'.\n";
877 }
878
879 file.closeGroup();
880}
881
882void LoadNexusLogs::loadSELog(Nexus::File &file, const std::string &absolute_entry_name,
883 const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
884 // Open the entry
885 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
886
887 file.openGroup(entry_name, "IXseblock");
888 std::string propName = entry_name;
889 if (workspace->run().hasProperty(propName)) {
890 propName = "selog_" + propName;
891 }
892 // There are two possible entries:
893 // value_log - A time series entry. This can contain a corrupt value entry
894 // so if it does use the value one
895 // value - A single value float entry
896 std::string const valueEntry = absolute_entry_name + "/value";
897 std::string const valueLogEntry = absolute_entry_name + "/value_log";
898 bool const foundValue = file.hasAddress(valueEntry);
899 bool const foundValueLog = file.hasAddress(valueLogEntry);
900
901 std::unique_ptr<Kernel::Property> logValue;
902 if (foundValueLog) {
903 try {
904 try {
905 file.openGroup("value_log", "NXlog");
906 } catch (Nexus::Exception const &) {
907 file.closeGroup();
908 throw;
909 }
910
911 logValue = createTimeSeries(file, propName, freqStart, g_log);
912 // Create (possibly) a boolean time series, companion to time series `logValue`.
913 auto validityLogValue = createTimeSeriesValidityFilter(file, *logValue, g_log);
914 if (validityLogValue) {
915 appendEndTimeLog(validityLogValue.get(), workspace->run());
916 workspace->mutableRun().addProperty(std::move(validityLogValue));
917 m_logsWithInvalidValues.emplace_back(propName);
918 }
919 appendEndTimeLog(logValue.get(), workspace->run());
920
921 file.closeGroup();
922 } catch (std::exception &e) {
923 g_log.warning() << "IXseblock entry '" << entry_name << "' gave an error when loading "
924 << "a time series:'" << e.what() << "'. Skipping entry\n";
925 file.closeGroup(); // value_log
926 file.closeGroup(); // entry_name
927 return;
928 }
929 } else if (foundValue) {
930 try {
931 // This may have a larger dimension than 1 bit it has no time field so
932 // take the first entry
933 file.openData("value");
934 Nexus::Info info = file.getInfo();
935 if (info.type == NXnumtype::FLOAT32) {
936 boost::scoped_array<float> value(new float[info.dims[0]]);
937 file.getData(value.get());
938 file.closeData();
939 logValue = std::make_unique<Kernel::PropertyWithValue<double>>(propName, static_cast<double>(value[0]), true);
940 } else {
941 file.closeGroup();
942 return;
943 }
944 } catch (Nexus::Exception const &e) {
945 g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading "
946 << "a single value:'" << e.what() << "'.\n";
947 file.closeData();
948 file.closeGroup();
949 return;
950 }
951 } else {
952 g_log.warning() << "IXseblock entry " << entry_name << " cannot be read, skipping entry.\n";
953 file.closeGroup();
954 return;
955 }
956 workspace->mutableRun().addProperty(std::move(logValue));
957 file.closeGroup();
958}
959
960} // 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 & g_log
Definition Algorithm.h:422
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:90
static std::string getInvalidValuesFilterLogName(const std::string &logName)
Gets the correct log name for the matching invalid values log for a given log name.
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 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 exec() override
Overwrites Algorithm method.
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:201
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
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.