16#include <Poco/DateTimeFormat.h>
17#include <Poco/DateTimeFormatter.h>
18#include <Poco/DateTimeParser.h>
20#include <boost/scoped_array.hpp>
29using namespace Kernel;
30using API::FileProperty;
31using API::MatrixWorkspace;
33using API::WorkspaceProperty;
35using Types::Core::DateAndTime;
45bool loadAndApplyMeasurementInfo(Nexus::File *
const file, API::MatrixWorkspace &
workspace) {
47 bool successfullyApplied =
false;
49 file->openGroup(
"measurement",
"NXcollection");
57 file->openData(
"label");
61 file->openData(
"subid");
65 file->openData(
"type");
70 successfullyApplied =
true;
71 }
catch (Nexus::Exception
const &) {
72 successfullyApplied =
false;
74 return successfullyApplied;
83bool loadAndApplyRunTitle(Nexus::File *
const file, API::MatrixWorkspace &
workspace) {
85 bool successfullyApplied =
false;
87 file->openData(
"title");
91 successfullyApplied =
true;
92 }
catch (Nexus::Exception
const &) {
93 successfullyApplied =
false;
95 return successfullyApplied;
111bool isControlValue(
const char &c,
const std::string &propName, Kernel::Logger &log) {
114 log.warning(
"Found an invalid character in property " + propName);
119 std::locale locale{};
121 return std::iscntrl(c, locale);
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");
143 file.getAttr(
"start", start);
144 }
catch (Nexus::Exception
const &) {
147 file.getAttr(
"offset", start);
148 }
catch (Nexus::Exception
const &) {
149 log.warning() <<
"Log entry has no start time indicated.\n";
154 if (start ==
"No 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")
166 throw Nexus::Exception(
"Unsupported time unit '" + time_units +
"'");
169 std::vector<double> time_double;
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";
178 log.debug() <<
" done reading \"time\" array\n";
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));
188 file.openData(
"value");
190 std::string value_units;
192 file.getAttr(
"units", value_units);
193 }
catch (Nexus::Exception
const &) {
199 Nexus::Info info = file.getInfo();
201 if (
size_t(info.dims[0]) != time_double.size()) {
203 throw Nexus::Exception(
"Invalid value entry for time series");
205 if (file.isDataInt())
207 std::vector<int> values;
209 file.getDataCoerce(values);
211 }
catch (Nexus::Exception
const &) {
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";
223 const int64_t item_length = info.dims[1];
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());
230 values = std::string(val_array.get(), total_length);
231 }
catch (Nexus::Exception
const &) {
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);
245 tsp->setUnits(value_units);
246 log.debug() <<
" done reading \"value\" array\n";
249 std::vector<double> values;
251 file.getDataCoerce(values);
253 }
catch (Nexus::Exception
const &) {
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";
263 throw Nexus::Exception(
"Invalid value type for time series. Only int, double or strings are "
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;
287 if (file.hasData(
"value_valid")) {
288 file.openData(
"value_valid");
291 Nexus::Info info = file.getInfo();
293 if (
size_t(info.dims[0]) != times.size()) {
294 throw Nexus::Exception(
"Invalid value entry for validity data");
296 if (file.isDataInt())
299 file.getDataCoerce(values);
301 }
catch (Nexus::Exception
const &) {
305 throw Nexus::Exception(
"Invalid value type for validity data. Only int is supported");
307 }
catch (std::exception
const &ex) {
308 std::string error_msg = ex.what();
309 log.warning() << error_msg <<
"\n";
312 return std::unique_ptr<Kernel::Property>(
nullptr);
316 bool invalidDataFound =
false;
317 boolValues.reserve(values.size());
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;
326 if (invalidDataFound) {
330 auto tsp = std::make_unique<TimeSeriesProperty<bool>>(tspName);
331 tsp->create(times, boolValues);
332 log.debug() <<
" done reading \"value_valid\" array\n";
337 return std::unique_ptr<Kernel::Property>(
nullptr);
355void appendEndTimeLog(Kernel::Property *prop,
const API::Run &run) {
357 if (prop->name() ==
"proton_charge")
361 auto tsLog =
dynamic_cast<TimeSeriesProperty<double> *
>(prop);
362 const auto endTime = run.endTime();
365 if (!tsLog || tsLog->size() == 0 || endTime <= tsLog->lastTime())
368 tsLog->addValue(endTime, tsLog->lastValue());
369 }
catch (
const Exception::NotFoundError &) {
371 }
catch (
const std::runtime_error &) {
382void readStartAndEndTime(Nexus::File &file, API::Run &run) {
385 file.openData(
"start_time");
386 Types::Core::DateAndTime start(file.getStrData());
388 file.openData(
"end_time");
389 Types::Core::DateAndTime end(file.getStrData());
391 run.setStartAndEndTime(start, end);
392 }
catch (Nexus::Exception
const &) {
404 "The name of the workspace that will be filled with the logs.");
405 const std::vector<std::string> exts{
".nxs",
".n*"};
407 "Path to the .nxs file to load. Can be an EventNeXus or a "
408 "histogrammed NeXus.");
410 "If true then some existing logs will be overwritten, if false they will "
413 "Entry in the nexus file from which to read the logs");
416 "If specified, only these logs will be loaded from the file (each "
417 "separated by a comma).");
420 "If specified, logs matching one of the patterns will NOT be loaded from the file (each "
421 "separated by a comma).");
437 std::vector<std::string> allow_list =
getProperty(
"AllowList");
438 std::vector<std::string> block_list =
getProperty(
"BlockList");
442 if (entry_name.empty()) {
445 Nexus::File file(filename);
448 file.openGroup(entry_name,
"NXentry");
450 throw std::invalid_argument(
"Unknown NeXus file format found in file '" + filename +
"', or '" + entry_name +
451 "' is not a valid NXentry");
457 file.openAddress(
"DASlogs");
459 file.openGroup(
"frequency",
"NXlog");
461 file.openData(
"time");
472 g_log.
warning() <<
"Log entry has no start time indicated.\n";
490 readStartAndEndTime(file,
workspace->mutableRun());
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.");
497 const std::map<std::string, std::set<std::string>> &allEntries =
getFileInfo()->getAllEntries();
499 auto lf_LoadLogsByClass = [&](
const std::string &group_class,
const bool isLog) {
500 auto itGroupClass = allEntries.find(group_class);
501 if (itGroupClass == allEntries.end()) {
504 const std::set<std::string> &entries = itGroupClass->second;
506 for (
const std::string &entry : entries) {
508 if (std::count(entry.begin(), entry.end(),
'/') == 2) {
517 lf_LoadLogsByClass(
"IXselog",
true);
518 lf_LoadLogsByClass(
"IXrunlog",
true);
519 lf_LoadLogsByClass(
"IXperiods",
false);
521 auto lf_LoadLogsByName = [&](
const std::string &group_name) {
522 for (
auto itGroupClass = allEntries.begin(); itGroupClass != allEntries.end(); ++itGroupClass) {
524 const std::string &group_class = itGroupClass->first;
525 const std::set<std::string> &entries = itGroupClass->second;
527 const std::string absoluteGroupName =
"/" + entry_name +
"/" + group_name;
528 auto itGroupName = entries.find(absoluteGroupName);
529 if (itGroupName == entries.end()) {
533 loadLogs(file, absoluteGroupName, group_class,
workspace, allow_list, block_list);
537 lf_LoadLogsByName(
"DASlogs");
538 lf_LoadLogsByName(
"framelog");
541 loadAndApplyMeasurementInfo(&file, *
workspace);
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";
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);
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);
577 "filtering events by time will not work \n";
579 file.openAddress(
"/" + entry_name);
580 if (!event_frame_number.empty())
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();
594 for (
auto number : event_frame_number) {
595 ptime.emplace_back(plogt[number]);
596 pval.emplace_back(plogv[number]);
598 pcharge->
create(ptime, pval);
600 workspace->mutableRun().addProperty(pcharge,
true);
607 file.openData(
"proton_charge");
608 std::vector<double> values;
609 file.getDataCoerce(values);
611 file.getAttr(
"units", units);
612 double charge = values.front();
613 if (units.find(
"picoCoulomb") != std::string::npos) {
614 charge *= 1.e-06 / 3600.;
616 workspace->mutableRun().setProtonCharge(charge);
621 workspace->mutableRun().getProtonCharge();
628 if (
workspace->run().hasProperty(
"proton_charge_by_period")) {
631 workspace->mutableRun().addProperty(pChargeUnfiltered,
true);
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"
649 <<
"\" contains invalid values, click \"Show Sample Logs\" "
655 <<
" contain invalid values, click \"Show Sample Logs\" for "
668 file.openGroup(
"Veto_pulse",
"NXgroup");
673 file.openData(
"veto_pulse_time");
676 std::string start_time;
677 file.getAttr(
"start_time", start_time);
678 DateAndTime start(start_time);
681 std::vector<double> time_double;
682 file.getData(time_double);
685 std::vector<double> values(time_double.size(), 0.0);
687 tsp->
create(start, time_double, values);
691 workspace->mutableRun().addProperty(tsp);
708 file.openGroup(
"periods",
"IXperiods");
709 file.openData(
"number");
710 file.getData(&
value);
719 const std::string nPeriodsLabel =
"nperiods";
727 file.openGroup(
"periods",
"IXperiods");
730 file.openData(
"number");
731 int numberOfPeriods = 0;
732 file.getData(&numberOfPeriods);
736 std::vector<double> protonChargeByPeriod(numberOfPeriods);
737 file.openData(
"proton_charge");
738 file.getDataCoerce(protonChargeByPeriod);
742 const std::string protonChargeByPeriodLabel =
"proton_charge_by_period";
748 this->
g_log.
debug(
"Cannot read periods information from the nexus file. "
749 "This group may be absent.");
751 }
catch (std::runtime_error &) {
752 this->
g_log.
debug(
"Cannot read periods information from the nexus file. "
753 "This group may be absent.");
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 {
773 const std::map<std::string, std::set<std::string>> &allEntries =
getFileInfo()->getAllEntries();
775 auto lf_LoadByLogClass = [&](
const std::string &logClass,
const bool isNxLog) {
776 auto itLogClass = allEntries.find(logClass);
777 if (itLogClass == allEntries.end()) {
780 const std::set<std::string> &logsSet = itLogClass->second;
781 auto itPrefixBegin = logsSet.lower_bound(absolute_entry_name);
783 if (allow_list.empty()) {
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); });
792 for (
auto it = itPrefixBegin;
793 it != logsSet.end() && it->compare(0, absolute_entry_name.size(), absolute_entry_name) == 0; ++it) {
795 if (std::count(it->begin(), it->end(),
'/') == 3) {
796 if (has_block_list) {
798 for (
auto &block : globblock_list) {
799 if (block->match((*it).substr((*it).find_last_of(
"/") + 1))) {
817 for (
const auto &allow : allow_list) {
818 itPrefixBegin = logsSet.find(absolute_entry_name +
"/" + allow);
819 if (itPrefixBegin == logsSet.end()) {
824 auto it = itPrefixBegin;
826 if (std::count(it->begin(), it->end(),
'/') == 3) {
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);
856 const std::shared_ptr<API::MatrixWorkspace> &
workspace)
const {
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);
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;
870 const std::map<std::string, std::set<std::string>> &allEntries =
getFileInfo()->getAllEntries();
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) {
877 if (entriesSet.count(valueEntry) == 1) {
880 if (entriesSet.count(validatorEntry) == 1) {
881 foundValidator =
true;
883 if (foundTime && foundValue && foundValidator) {
888 if (!foundTime || !foundValue) {
889 g_log.
warning() <<
"Invalid NXlog entry " << entry_name <<
" found. Did not contain 'value' and 'time'.\n";
895 bool overwritelogs = this->
getProperty(
"OverwriteLogs");
897 if (overwritelogs || !(
workspace->run().hasProperty(entry_name))) {
898 auto logValue = createTimeSeries(file, entry_name,
freqStart,
g_log);
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);
908 appendEndTimeLog(logValue.get(),
workspace->run());
909 workspace->mutableRun().addProperty(std::move(logValue), overwritelogs);
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";
921 const std::shared_ptr<API::MatrixWorkspace> &
workspace)
const {
923 const std::string entry_name = absolute_entry_name.substr(absolute_entry_name.find_last_of(
"/") + 1);
925 file.openGroup(entry_name,
"IXseblock");
926 std::string propName = entry_name;
927 if (
workspace->run().hasProperty(propName)) {
928 propName =
"selog_" + propName;
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;
939 const std::map<std::string, std::set<std::string>> &allEntries =
getFileInfo()->getAllEntries();
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) {
946 if (entriesSet.count(valueLogEntry) == 1) {
947 foundValueLog =
true;
953 std::unique_ptr<Kernel::Property> logValue;
957 file.openGroup(
"value_log",
"NXlog");
965 auto validityLogValue = createTimeSeriesValidityFilter(file, *logValue,
g_log);
966 if (validityLogValue) {
967 appendEndTimeLog(validityLogValue.get(),
workspace->run());
968 workspace->mutableRun().addProperty(std::move(validityLogValue));
971 appendEndTimeLog(logValue.get(),
workspace->run());
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";
981 }
else if (foundValue) {
985 file.openData(
"value");
988 boost::scoped_array<float>
value(
new float[info.
dims[0]]);
989 file.getData(
value.get());
991 logValue = std::make_unique<Kernel::PropertyWithValue<double>>(propName,
static_cast<double>(
value[0]),
true);
997 g_log.
warning() <<
"IXseblock entry " << entry_name <<
" gave an error when loading "
998 <<
"a single value:'" << e.what() <<
"'.\n";
1004 g_log.
warning() <<
"IXseblock entry " << entry_name <<
" cannot be read, skipping entry.\n";
1008 workspace->mutableRun().addProperty(std::move(logValue));
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
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.
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.
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.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
void warning(const std::string &msg)
Logs at warning level.
The concrete, templated class for properties.
virtual void setUnits(const std::string &unit)
Sets the units of the property, as a string.
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.
@ Input
An input workspace.
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.