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 if (file.isDataInt()) { // Int type
201 // Check the size
202 if (size_t(info.dims[0]) != time_double.size()) {
203 file.closeData();
204 throw Nexus::Exception("Invalid value entry for time series");
205 }
206 std::vector<int> values;
207 try {
208 file.getDataCoerce(values);
209 file.closeData();
210 } catch (Nexus::Exception const &) {
211 file.closeData();
212 throw;
213 }
214 // Make an int TSP
215 auto tsp = std::make_unique<TimeSeriesProperty<int>>(propName);
216 tsp->create(start_time, time_double, values);
217 tsp->setUnits(value_units);
218 log.debug() << " done reading \"value\" array\n";
219 return tsp;
220 } else if (info.type == NXnumtype::CHAR) {
221 std::string values;
222 // the length of each string is normally stored in the second dimension (1),
223 // but some older files (wrongly) put this in the first dimension (0) if the rank is 1
224 const int64_t item_length = (info.dims.size() > 1 ? info.dims[1] : info.dims[0]);
225 try {
226 const std::size_t total_length =
227 std::accumulate(info.dims.cbegin(), info.dims.cend(), std::size_t(1),
228 [](std::size_t acc, int64_t x) { return acc * static_cast<std::size_t>(x); });
229 boost::scoped_array<char> val_array(new char[total_length + 1]);
230 file.getData(val_array.get());
231 file.closeData();
232 values = std::string(val_array.get(), total_length);
233 } catch (Nexus::Exception const &) {
234 file.closeData();
235 throw;
236 }
237 // The string may contain non-printable (i.e. control) characters, replace these
238 std::replace_if(values.begin(), values.end(), [&](const char &c) { return isControlValue(c, propName, log); }, ' ');
239 auto tsp = std::make_unique<TimeSeriesProperty<std::string>>(propName);
240 std::vector<DateAndTime> times;
241 DateAndTime::createVector(start_time, time_double, times);
242 const size_t ntimes = times.size();
243 for (size_t i = 0; i < ntimes; ++i) {
244 std::string value_i = std::string(values.data() + i * item_length, item_length);
245 tsp->addValue(times[i], value_i);
246 }
247 tsp->setUnits(value_units);
248 log.debug() << " done reading \"value\" array\n";
249 return tsp;
250 } else if (info.type == NXnumtype::FLOAT32 || info.type == NXnumtype::FLOAT64) {
251 std::vector<double> values;
252 try {
253 file.getDataCoerce(values);
254 file.closeData();
255 } catch (Nexus::Exception const &) {
256 file.closeData();
257 throw;
258 }
259 auto tsp = std::make_unique<TimeSeriesProperty<double>>(propName);
260 tsp->create(start_time, time_double, values);
261 tsp->setUnits(value_units);
262 log.debug() << " done reading \"value\" array\n";
263 return tsp;
264 } else {
265 throw Nexus::Exception("Invalid value type for time series. Only int, double or strings are "
266 "supported");
267 }
268}
269
280std::unique_ptr<Kernel::Property> createTimeSeriesValidityFilter(Nexus::File &file, const Kernel::Property &prop,
281 Kernel::Logger &log) {
282 const auto tsProp = dynamic_cast<const Kernel::ITimeSeriesProperty *>(&prop);
283 const auto times = tsProp->timesAsVector();
284 std::vector<int> values;
285 std::vector<bool> boolValues;
286 // Now the validity of the values
287 // this should be a match int array to the data values (or times)
288 // If not present assume all data is valid
289 if (file.hasData("value_valid")) {
290 file.openData("value_valid");
291 try {
292 // Now the validity data
293 Nexus::Info info = file.getInfo();
294 // Check the size
295 if (size_t(info.dims[0]) != times.size()) {
296 throw Nexus::Exception("Invalid value entry for validity data");
297 }
298 if (file.isDataInt()) // Int type
299 {
300 try {
301 file.getDataCoerce(values);
302 file.closeData();
303 } catch (Nexus::Exception const &) {
304 throw;
305 }
306 } else {
307 throw Nexus::Exception("Invalid value type for validity data. Only int is supported");
308 }
309 } catch (std::exception const &ex) {
310 std::string error_msg = ex.what();
311 log.warning() << error_msg << "\n";
312 file.closeData();
313 // no data found
314 return std::unique_ptr<Kernel::Property>(nullptr);
315 }
316 }
317
318 bool invalidDataFound = false;
319 boolValues.reserve(values.size());
320 // convert the integer values to boolean with 0=invalid data
321 for (size_t i = 0; i < values.size(); i++) {
322 bool isInvalidData = (values[i] == 0);
323 boolValues.emplace_back(!isInvalidData);
324 if (isInvalidData && !invalidDataFound) {
325 invalidDataFound = true;
326 }
327 }
328 if (invalidDataFound) {
329 // Prepare the TimeSeriesProperty<bool>
330 // It's name will be the name of the property plus suffix "_invalid_values"
331 const auto tspName = API::LogManager::getInvalidValuesFilterLogName(prop.name());
332 auto tsp = std::make_unique<TimeSeriesProperty<bool>>(tspName);
333 tsp->create(times, boolValues);
334 log.debug() << " done reading \"value_valid\" array\n";
335
336 return tsp;
337 } else {
338 // no data found
339 return std::unique_ptr<Kernel::Property>(nullptr);
340 }
341}
342
357void appendEndTimeLog(Kernel::Property *prop, const API::Run &run) {
358 // do not modify proton charge
359 if (prop->name() == "proton_charge")
360 return;
361
362 try {
363 auto tsLog = dynamic_cast<TimeSeriesProperty<double> *>(prop);
364 const auto endTime = run.endTime();
365
366 // First check if it is valid to append a log entry
367 if (!tsLog || tsLog->size() == 0 || endTime <= tsLog->lastTime())
368 return;
369
370 tsLog->addValue(endTime, tsLog->lastValue());
371 } catch (const Exception::NotFoundError &) {
372 // pass
373 } catch (const std::runtime_error &) {
374 // pass
375 }
376}
377
384void readStartAndEndTime(Nexus::File &file, API::Run &run) {
385 try {
386 // Read the start and end time strings
387 file.openData("start_time");
388 Types::Core::DateAndTime start(file.getStrData());
389 file.closeData();
390 file.openData("end_time");
391 Types::Core::DateAndTime end(file.getStrData());
392 file.closeData();
393 run.setStartAndEndTime(start, end);
394 } catch (Nexus::Exception const &) {
395 }
396}
397
398} // End of anonymous namespace
399
402
405 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("Workspace", "Anonymous", Direction::InOut),
406 "The name of the workspace that will be filled with the logs.");
407 const std::vector<std::string> exts{".nxs", ".n*"};
408 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts),
409 "Path to the .nxs file to load. Can be an EventNeXus or a "
410 "histogrammed NeXus.");
411 declareProperty(std::make_unique<PropertyWithValue<bool>>("OverwriteLogs", true, Direction::Input),
412 "If true then some existing logs will be overwritten, if false they will "
413 "not.");
414 declareProperty(std::make_unique<PropertyWithValue<std::string>>("NXentryName", "", Direction::Input),
415 "Entry in the nexus file from which to read the logs");
416 declareProperty(std::make_unique<PropertyWithValue<std::vector<std::string>>>("AllowList", std::vector<std::string>(),
418 "If specified, only these logs will be loaded from the file (each "
419 "separated by a comma).");
420 declareProperty(std::make_unique<PropertyWithValue<std::vector<std::string>>>("BlockList", std::vector<std::string>(),
422 "If specified, logs matching one of the patterns will NOT be loaded from the file (each "
423 "separated by a comma).");
424}
425
434 std::string filename = getPropertyValue("Filename");
436
437 std::string entry_name = getPropertyValue("NXentryName");
438
439 std::vector<std::string> allow_list = getProperty("AllowList");
440 std::vector<std::string> block_list = getProperty("BlockList");
441
442 // Find the entry name to use (normally "entry" for SNS, "raw_data_1" for
443 // ISIS) if entry name is empty
444 if (entry_name.empty()) {
445 entry_name = LoadTOFRawNexus::getEntryName(filename);
446 }
447 Nexus::File file(filename);
448 // Find the root entry
449 try {
450 file.openGroup(entry_name, "NXentry");
451 } catch (Nexus::Exception const &) {
452 throw std::invalid_argument("Unknown NeXus file format found in file '" + filename + "', or '" + entry_name +
453 "' is not a valid NXentry");
454 }
455
458 try {
459 file.openAddress("DASlogs");
460 try {
461 file.openGroup("frequency", "NXlog");
462 try {
463 file.openData("time");
464
465 //----- Start time is an ISO8601 string date and time. ------
466 try {
467 file.getAttr("start", freqStart);
468
469 } catch (Nexus::Exception const &) {
470 // Some logs have "offset" instead of start
471 try {
472 file.getAttr("offset", freqStart);
473 } catch (Nexus::Exception const &) {
474 g_log.warning() << "Log entry has no start time indicated.\n";
475 file.closeData();
476 throw;
477 }
478 }
479 file.closeData();
480 } catch (Nexus::Exception const &) {
481 // No time. This is not an SNS SNAP file
482 }
483 file.closeGroup();
484 } catch (Nexus::Exception const &) {
485 // No time. This is not an SNS frequency group
486 }
487 file.closeGroup();
488 } catch (Nexus::Exception const &) {
489 // No time. This is not an SNS group
490 }
491
492 readStartAndEndTime(file, workspace->mutableRun());
493
494 if (!allow_list.empty() && !block_list.empty()) {
495 throw std::runtime_error("BlockList and AllowList are mutually exclusive! "
496 "Please only enter values for one of these fields.");
497 }
498
499 auto lf_LoadLogsByClass = [&](const std::string &group_class, const bool isLog) {
500 const std::set<std::string> &entries = file.getEntriesByClass(group_class);
501 // still a linear search
502 for (const std::string &entry : entries) {
503 // match for 2nd level entry /a/b
504 if (std::count(entry.begin(), entry.end(), '/') == 2) {
505 if (isLog) {
506 loadLogs(file, entry, group_class, workspace, allow_list, block_list);
507 } else {
508 loadNPeriods(file, workspace);
509 }
510 }
511 }
512 };
513 lf_LoadLogsByClass("IXselog", true);
514 lf_LoadLogsByClass("IXrunlog", true);
515 lf_LoadLogsByClass("IXperiods", false);
516
517 auto lf_LoadLogsByName = [&](const std::string &group_name) {
518 std::string const absoluteGroupName = "/" + entry_name + "/" + group_name;
519 if (file.hasAddress(absoluteGroupName)) {
520 std::string const &group_class = file.classForEntry(absoluteGroupName);
521 loadLogs(file, absoluteGroupName, group_class, workspace, allow_list, block_list);
522 }
523 };
524
525 lf_LoadLogsByName("DASlogs");
526 lf_LoadLogsByName("framelog");
527
528 // If there's measurement information, load that info as logs.
529 loadAndApplyMeasurementInfo(&file, *workspace);
530 // If there's title information, load that info as logs.
531 loadAndApplyRunTitle(&file, *workspace);
532
533 // Freddie Akeroyd 12/10/2011
534 // current ISIS implementation contains an additional indirection between
535 // collected frames via an
536 // "event_frame_number" array in NXevent_data (which eliminates frames with no
537 // events).
538 // the proton_log is for all frames and so is longer than the event_index
539 // array, so we need to
540 // filter the proton_charge log based on event_frame_number
541 // This difference will be removed in future for compatibility with SNS, but
542 // the code below will allow current SANS2D files to load
543 if (workspace->mutableRun().hasProperty("proton_log")) {
544 std::vector<int> event_frame_number;
545 this->getLogger().notice() << "Using old ISIS proton_log and event_frame_number indirection...\n";
546 try {
547 // Find the bank/name corresponding to the first event data entry, i.e.
548 // one with type NXevent_data.
549 file.openAddress("/" + entry_name);
550 std::set<std::string> const &events = file.getEntriesByClass("NXevent_data");
551 for (std::string const &event : events) {
552 std::string const eventEntry = event.substr(event.find_last_of("/") + 1);
553
554 this->getLogger().debug() << "Opening"
555 << " /" + entry_name + "/" + eventEntry + "/event_frame_number"
556 << " to find the event_frame_number\n";
557 file.openAddress("/" + entry_name + "/" + eventEntry + "/event_frame_number");
558 file.getData(event_frame_number);
559 }
560 } catch (Nexus::Exception const &) {
561 this->getLogger().warning() << "Unable to load event_frame_number - "
562 "filtering events by time will not work \n";
563 }
564 file.openAddress("/" + entry_name);
565 if (!event_frame_number.empty()) // ISIS indirection - see above comments
566 {
568 dynamic_cast<Kernel::TimeSeriesProperty<double> *>(workspace->mutableRun().getProperty("proton_log"));
569 if (!plog)
570 throw std::runtime_error("Could not cast (interpret) proton_log as a time "
571 "series property. Cannot continue.");
573 std::vector<double> pval;
574 std::vector<Mantid::Types::Core::DateAndTime> ptime;
575 pval.reserve(event_frame_number.size());
576 ptime.reserve(event_frame_number.size());
577 std::vector<Mantid::Types::Core::DateAndTime> plogt = plog->timesAsVector();
578 std::vector<double> plogv = plog->valuesAsVector();
579 for (auto number : event_frame_number) {
580 ptime.emplace_back(plogt[number]);
581 pval.emplace_back(plogv[number]);
582 }
583 pcharge->create(ptime, pval);
584 pcharge->setUnits("uAh");
585 workspace->mutableRun().addProperty(pcharge, true);
586 }
587 }
588
589 if (!workspace->run().hasProperty(workspace->run().getProtonChargeLogName())) {
590 try {
591 // Try pulling proton charge from the main proton_charge entry
592 file.openData("proton_charge");
593 std::vector<double> values;
594 file.getDataCoerce(values);
595 std::string units;
596 file.getAttr("units", units);
597 double charge = values.front();
598 if (units.find("picoCoulomb") != std::string::npos) {
599 charge *= 1.e-06 / 3600.;
600 }
601 workspace->mutableRun().setProtonCharge(charge);
602 } catch (Nexus::Exception const &) {
603 // Try and integrate the proton logs
604 try {
605 // Use the DAS logs to integrate the proton charge (if any).
606 workspace->mutableRun().getProtonCharge();
607 } catch (Exception::NotFoundError &) {
608 // Ignore not found property error.
609 }
610 }
611 }
612 // For period data mark proton charge log value as unfiltered to enable subsequent filtering by period.
613 if (workspace->run().hasProperty("proton_charge_by_period")) {
614 Kernel::PropertyWithValue<int> *pChargeUnfiltered =
615 new Kernel::PropertyWithValue<int>(workspace->run().getProtonChargeUnfilteredName(), 1);
616 workspace->mutableRun().addProperty(pChargeUnfiltered, true);
617 }
618
619 if (!allow_list.empty()) {
620 for (const auto &allow : allow_list) {
621 if (!workspace->run().hasProperty(allow)) {
622 g_log.notice() << "could not load entry '" << allow << "' that was specified in the allow list"
623 << "\n";
624 }
625 }
626 }
627
628 // Close the file
629 file.close();
630
631 if (m_logsWithInvalidValues.size() > 0) {
632 if (m_logsWithInvalidValues.size() == 1) {
633 g_log.warning() << "Sample Log \"" << m_logsWithInvalidValues[0]
634 << "\" contains invalid values, click \"Show Sample Logs\" "
635 "for details.\n";
636 }
637 auto other_string = (m_logsWithInvalidValues.size() < 2) ? " other " : " others";
638 g_log.warning() << "Sample Log \"" << m_logsWithInvalidValues[0] << "\" and " << m_logsWithInvalidValues.size() - 1
639 << other_string
640 << " contain invalid values, click \"Show Sample Logs\" for "
641 "details.\n";
642 }
643}
644
651void LoadNexusLogs::loadVetoPulses(Nexus::File &file, const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
652 try {
653 file.openGroup("Veto_pulse", "NXgroup");
654 } catch (Nexus::Exception const &) {
655 // No group. This is common in older files
656 return;
657 }
658 file.openData("veto_pulse_time");
659
660 // Load the start date/time as ISO8601 string.
661 std::string start_time;
662 file.getAttr("start_time", start_time);
663 DateAndTime start(start_time);
664
665 // Read the offsets
666 std::vector<double> time_double;
667 file.getData(time_double);
668
669 // Fake values with zeroes.
670 std::vector<double> values(time_double.size(), 0.0);
671 TimeSeriesProperty<double> *tsp = new TimeSeriesProperty<double>("veto_pulse_time");
672 tsp->create(start, time_double, values);
673 tsp->setUnits("");
674
675 // Add the log
676 workspace->mutableRun().addProperty(tsp);
677
678 file.closeData();
679 file.closeGroup();
680}
681
690void LoadNexusLogs::loadNPeriods(Nexus::File &file, const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
691 int value = 1; // Default to 1-period unless
692 try {
693 file.openGroup("periods", "IXperiods");
694 file.openData("number");
695 file.getData(&value);
696 file.closeData();
697 file.closeGroup();
698 } catch (Nexus::Exception const &) {
699 // Likely missing IXperiods.
700 return;
701 }
702
703 API::Run &run = workspace->mutableRun();
704 const std::string nPeriodsLabel = "nperiods";
705 if (!run.hasProperty(nPeriodsLabel)) {
706 run.addProperty(new PropertyWithValue<int>(nPeriodsLabel, value));
707 }
708
709 // For ISIS Nexus only, fabricate an additional log containing an array of
710 // proton charge information from the periods group.
711 try {
712 file.openGroup("periods", "IXperiods");
713
714 // Get the number of periods again
715 file.openData("number");
716 int numberOfPeriods = 0;
717 file.getData(&numberOfPeriods);
718 file.closeData();
719
720 // Get the proton charge vector
721 std::vector<double> protonChargeByPeriod(numberOfPeriods);
722 file.openData("proton_charge");
723 file.getDataCoerce(protonChargeByPeriod);
724 file.closeData();
725
726 // Add the proton charge vector
727 const std::string protonChargeByPeriodLabel = "proton_charge_by_period";
728 if (!run.hasProperty(protonChargeByPeriodLabel)) {
729 run.addProperty(new ArrayProperty<double>(protonChargeByPeriodLabel, std::move(protonChargeByPeriod)));
730 }
731 file.closeGroup();
732 } catch (Nexus::Exception const &) {
733 this->g_log.debug("Cannot read periods information from the nexus file. "
734 "This group may be absent.");
735 file.closeGroup();
736 } catch (std::runtime_error &) {
737 this->g_log.debug("Cannot read periods information from the nexus file. "
738 "This group may be absent.");
739 file.closeGroup();
740 }
741}
742
753void LoadNexusLogs::loadLogs(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class,
754 const std::shared_ptr<API::MatrixWorkspace> &workspace,
755 const std::vector<std::string> &allow_list,
756 const std::vector<std::string> &block_list) const {
757
758 auto lf_LoadByLogClass = [&](std::string const &logClass, const bool isNxLog) {
759 std::set<std::string> const &logsSet = file.getEntriesByClass(logClass);
760 if (logsSet.empty()) {
761 return;
762 }
763 auto itPrefixBegin = logsSet.lower_bound(absolute_entry_name);
764
765 if (allow_list.empty()) {
766 // convert the blocklist into a bunch of objects to handle globbing
767 const bool has_block_list = (!block_list.empty());
768 std::vector<std::unique_ptr<Poco::Glob>> globblock_list;
769 if (has_block_list) {
770 std::transform(block_list.cbegin(), block_list.cend(), std::back_inserter(globblock_list),
771 [](const auto &block) { return std::make_unique<Poco::Glob>(block); });
772 }
773
774 for (auto it = itPrefixBegin;
775 it != logsSet.end() && it->compare(0, absolute_entry_name.size(), absolute_entry_name) == 0; ++it) {
776 // must be third level entry
777 if (std::count(it->begin(), it->end(), '/') == 3) {
778 if (has_block_list) {
779 bool skip = false;
780 for (auto &block : globblock_list) {
781 if (block->match((*it).substr((*it).find_last_of("/") + 1))) {
782 skip = true;
783 break; // from the loop of block items
784 }
785 }
786 if (skip) {
787 continue; // go to next log
788 }
789 } // end of looping over block_list
790
791 if (isNxLog) {
792 loadNXLog(file, *it, logClass, workspace);
793 } else {
794 loadSELog(file, *it, workspace);
795 }
796 }
797 }
798 } else {
799 for (const auto &allow : allow_list) {
800 itPrefixBegin = logsSet.find(absolute_entry_name + "/" + allow);
801 if (itPrefixBegin == logsSet.end()) {
802 // don't print warning yet since it might be found in another log
803 // class
804 continue;
805 }
806 auto it = itPrefixBegin;
807 // must be third level entry
808 if (std::count(it->begin(), it->end(), '/') == 3) {
809 if (isNxLog) {
810 loadNXLog(file, *it, logClass, workspace);
811 } else {
812 loadSELog(file, *it, workspace);
813 }
814 }
815 }
816 }
817 };
818
819 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
820 file.openGroup(entry_name, entry_class);
821 lf_LoadByLogClass("NXlog", true);
822 lf_LoadByLogClass("NXpositioner", true);
823 lf_LoadByLogClass("IXseblock", false);
825
826 file.closeGroup();
827}
828
837void LoadNexusLogs::loadNXLog(Nexus::File &file, const std::string &absolute_entry_name, const std::string &entry_class,
838 const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
839
840 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
841 g_log.debug() << "processing " << entry_name << ":" << entry_class << "\n";
842 file.openGroup(entry_name, entry_class);
843 // Validate the NX log class.
844 // Just verify that time and value entries exist
845 std::string const timeEntry = absolute_entry_name + "/time";
846 std::string const valueEntry = absolute_entry_name + "/value";
847 std::string const validatorEntry = absolute_entry_name + "/value_valid";
848 bool const foundValue = file.hasAddress(valueEntry);
849 bool const foundTime = file.hasAddress(timeEntry);
850 bool const foundValidator = file.hasAddress(validatorEntry);
851
852 if (!foundTime || !foundValue) {
853 g_log.warning() << "Invalid NXlog entry " << entry_name << " found. Did not contain 'value' and 'time'.\n";
854 file.closeGroup();
855 return;
856 }
857
858 // whether to overwrite logs on workspace
859 bool overwritelogs = this->getProperty("OverwriteLogs");
860 try {
861 if (overwritelogs || !(workspace->run().hasProperty(entry_name))) {
862 auto logValue = createTimeSeries(file, entry_name, freqStart, g_log);
863 // Create (possibly) a boolean time series, companion to time series `entry_name`
864 if (foundValidator) {
865 auto validityLogValue = createTimeSeriesValidityFilter(file, *logValue, g_log);
866 if (validityLogValue) {
867 appendEndTimeLog(validityLogValue.get(), workspace->run());
868 workspace->mutableRun().addProperty(std::move(validityLogValue), overwritelogs);
869 m_logsWithInvalidValues.emplace_back(entry_name);
870 }
871 }
872 appendEndTimeLog(logValue.get(), workspace->run());
873 workspace->mutableRun().addProperty(std::move(logValue), overwritelogs);
874 }
875 } catch (Nexus::Exception const &e) {
876 g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading:'" << e.what() << "'.\n";
877 } catch (std::invalid_argument &e) {
878 g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading:'" << e.what() << "'.\n";
879 }
880
881 file.closeGroup();
882}
883
884void LoadNexusLogs::loadSELog(Nexus::File &file, const std::string &absolute_entry_name,
885 const std::shared_ptr<API::MatrixWorkspace> &workspace) const {
886 // Open the entry
887 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of("/") + 1);
888
889 file.openGroup(entry_name, "IXseblock");
890 std::string propName = entry_name;
891 if (workspace->run().hasProperty(propName)) {
892 propName = "selog_" + propName;
893 }
894 // There are two possible entries:
895 // value_log - A time series entry. This can contain a corrupt value entry
896 // so if it does use the value one
897 // value - A single value float entry
898 std::string const valueEntry = absolute_entry_name + "/value";
899 std::string const valueLogEntry = absolute_entry_name + "/value_log";
900 bool const foundValue = file.hasAddress(valueEntry);
901 bool const foundValueLog = file.hasAddress(valueLogEntry);
902
903 std::unique_ptr<Kernel::Property> logValue;
904 if (foundValueLog) {
905 try {
906 try {
907 file.openGroup("value_log", "NXlog");
908 } catch (Nexus::Exception const &) {
909 file.closeGroup();
910 throw;
911 }
912
913 logValue = createTimeSeries(file, propName, freqStart, g_log);
914 // Create (possibly) a boolean time series, companion to time series `logValue`.
915 auto validityLogValue = createTimeSeriesValidityFilter(file, *logValue, g_log);
916 if (validityLogValue) {
917 appendEndTimeLog(validityLogValue.get(), workspace->run());
918 workspace->mutableRun().addProperty(std::move(validityLogValue));
919 m_logsWithInvalidValues.emplace_back(propName);
920 }
921 appendEndTimeLog(logValue.get(), workspace->run());
922
923 file.closeGroup();
924 } catch (std::exception &e) {
925 g_log.warning() << "IXseblock entry '" << entry_name << "' gave an error when loading "
926 << "a time series:'" << e.what() << "'. Skipping entry\n";
927 file.closeGroup(); // value_log
928 file.closeGroup(); // entry_name
929 return;
930 }
931 } else if (foundValue) {
932 try {
933 // This may have a larger dimension than 1 bit it has no time field so
934 // take the first entry
935 file.openData("value");
936 Nexus::Info info = file.getInfo();
937 if (info.type == NXnumtype::FLOAT32) {
938 boost::scoped_array<float> value(new float[info.dims[0]]);
939 file.getData(value.get());
940 file.closeData();
941 logValue = std::make_unique<Kernel::PropertyWithValue<double>>(propName, static_cast<double>(value[0]), true);
942 } else {
943 file.closeGroup();
944 return;
945 }
946 } catch (Nexus::Exception const &e) {
947 g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading "
948 << "a single value:'" << e.what() << "'.\n";
949 file.closeData();
950 file.closeGroup();
951 return;
952 }
953 } else {
954 g_log.warning() << "IXseblock entry " << entry_name << " cannot be read, skipping entry.\n";
955 file.closeGroup();
956 return;
957 }
958 workspace->mutableRun().addProperty(std::move(logValue));
959 file.closeGroup();
960}
961
962} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
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:423
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.