Mantid
Loading...
Searching...
No Matches
AddSampleLog.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 +
11#include "MantidAPI/Run.h"
12#include "MantidAPI/Workspace.h"
22
23#include <string>
24
25namespace {
26
27const std::string NumberType("NumberType");
28const std::vector<std::string> typeOptions{"AutoDetect", "Int", "Double"};
29enum class TypeMode { AUTO_DETECT, INT, DOUBLE, enum_count };
31
32const std::string LogType("LogType");
33const std::vector<std::string> propOptions{"String", "Number", "Number Series"};
34enum class LogMode { STRING_LOG, NUMBER_LOG, NUMBER_SERIES_LOG, enum_count };
36} // namespace
37
38namespace Mantid::Algorithms {
39
40// Register the algorithm into the AlgorithmFactory
41DECLARE_ALGORITHM(AddSampleLog)
42
43using namespace Kernel;
44using namespace API;
45using Types::Core::DateAndTime;
46
48 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace", "", Direction::InOut),
49 "Workspace to add the log entry to");
50 declareProperty("LogName", "", std::make_shared<MandatoryValidator<std::string>>(),
51 "The name that will identify the log entry");
52
53 declareProperty("LogText", "", "The content of the log");
54
56 "The type that the log data will be.");
57 declareProperty("LogUnit", "", "The units of the log");
58
60 "Force LogText to be interpreted as a number of type 'int' "
61 "or 'double'.");
62
63 // add optional workspace which contains the data of the TimeSeriesProperty
64 declareProperty(std::make_unique<WorkspaceProperty<API::MatrixWorkspace>>("TimeSeriesWorkspace", "", Direction::Input,
66 "Optional workspace contain the data");
67 declareProperty("WorkspaceIndex", 0, "The workspace index of the TimeSeriesWorkspace to be imported.");
68 declareProperty("AutoMetaData", false,
69 "If it is specified as true, then all the meta data "
70 "information will be retrieved from the input workspace. It "
71 "will be used with algorithm ExportTimeSeriesProperty.");
72
73 std::vector<std::string> time_units{"Second", "Nanosecond"};
74 declareProperty("TimeUnit", "Second", std::make_shared<Kernel::StringListValidator>(time_units),
75 "The unit of the time of the input workspace");
76 declareProperty("RelativeTime", true,
77 "If specified as True, then then the "
78 "time stamps are relative to the run "
79 "start time of the target workspace.");
80 declareProperty("UpdateInstrumentParameters", false,
81 "Update instrument parameters to account for new log. "
82 "This will update detector positions that depend on motors, for example.");
83}
84
89 // A pointer to the workspace to add a log to
90 Workspace_sptr target_workspace = getProperty("Workspace");
91 auto expinfo_ws = std::dynamic_pointer_cast<ExperimentInfo>(target_workspace);
92 if (!expinfo_ws) {
93 // We're dealing with an MD workspace which has multiple experiment infos
94 auto infos = std::dynamic_pointer_cast<MultipleExperimentInfos>(target_workspace);
95 if (!infos) {
96 throw std::invalid_argument("Input workspace does not support sample logs");
97 }
98 if (infos->getNumExperimentInfo() < 1) {
100 infos->addExperimentInfo(info);
101 }
102 expinfo_ws = infos->getExperimentInfo(0);
103 }
104 // we're going to edit the workspaces run details so get a non-const reference
105 // to it
106 Run &theRun = expinfo_ws->mutableRun();
107 // get the data that the user wants to add
108 std::string propName = getProperty("LogName");
109 std::string propValue = getProperty("LogText");
110 std::string propUnit = getProperty("LogUnit");
111 LOGMODE propType = getPropertyValue(LogType);
112 TYPEMODE propNumberType = getPropertyValue("NumberType");
113
114 // check inputs
115 if ((propNumberType != TypeMode::AUTO_DETECT) && (propType == LogMode::STRING_LOG)) {
116 throw std::invalid_argument("You may only use NumberType 'Int' or 'Double' options if "
117 "LogType is 'Number' or 'Number Series'");
118 }
119
120 // Remove any existing log
121 if (theRun.hasProperty(propName)) {
122 theRun.removeLogData(propName);
123 }
124
125 // add sample log!
126 if (propType == LogMode::STRING_LOG) {
127 // add string log value and return
128 addStringLog(theRun, propName, propValue, propUnit);
129 } else if (propType == LogMode::NUMBER_SERIES_LOG) {
130 // add a TimeSeriesProperty
131 // TODO: Need to re-define the behavior on the default propNumberType for
132 // Series.
133 // If propValue is given, then use this value to determine whether the
134 // type is
135 // integer or double; Otherwise, the series should be double
136 addTimeSeriesProperty(theRun, propName, propValue, propUnit, propNumberType);
137 } else {
138 // add a single value property
139 addSingleValueProperty(theRun, propName, propValue, propUnit, propNumberType);
140 }
141
142 // update parameters based on the new log information if requested
143 const bool updateInstrumentParams = getProperty("UpdateInstrumentParameters");
144 if (updateInstrumentParams && expinfo_ws->getInstrument()->isParametrized())
145 expinfo_ws->populateInstrumentParameters();
146}
147
156void AddSampleLog::addSingleValueProperty(Run &theRun, const std::string &propName, const std::string &propValue,
157 const std::string &propUnit, const std::string &propNumberType) {
158 // add a single value property, integer or double
159 bool value_is_int(false);
160 TYPEMODE propType = propNumberType;
161 if (propType != TypeMode::AUTO_DETECT) {
162 value_is_int = (propType == TypeMode::INT);
163 } else {
164 int intVal;
165 if (Strings::convert(propValue, intVal)) {
166 value_is_int = true;
167 }
168 }
169
170 // set value
171 if (value_is_int) {
172 // convert to integer
173 int intVal;
174 int convert_to_int = Strings::convert(propValue, intVal);
175 if (convert_to_int == 0) {
176 // spit out error message and set to default value
177 g_log.error() << "Error interpreting string '" << propValue << "' as NumberType Int.";
178 throw std::runtime_error("Invalie integer input");
179 }
180 theRun.addLogData(new PropertyWithValue<int>(propName, intVal));
181 } else {
182 // convert to double
183 double dblVal;
184 int convert_to_dbl = Strings::convert(propValue, dblVal);
185 if (convert_to_dbl == 0) {
186 g_log.error() << "Error interpreting string '" << propValue << "' as NumberType Double.";
187 throw std::runtime_error("Invalid double input.");
188 }
189 theRun.addLogData(new PropertyWithValue<double>(propName, dblVal));
190 g_log.information() << "added property " << propName << " with value " << dblVal << "\n";
191 }
192
193 // add unit
194 theRun.getProperty(propName)->setUnits(propUnit);
195
196 return;
197}
198
206void AddSampleLog::addStringLog(Run &theRun, const std::string &propName, const std::string &propValue,
207 const std::string &propUnit) {
208 theRun.addLogData(new PropertyWithValue<std::string>(propName, propValue));
209 theRun.getProperty(propName)->setUnits(propUnit);
210 return;
211}
212
221void AddSampleLog::addTimeSeriesProperty(Run &run_obj, const std::string &prop_name, const std::string &prop_value,
222 const std::string &prop_unit, const std::string &prop_number_type) {
223 // set up the number type right
224 bool is_int_series(false);
225 TYPEMODE propType = prop_number_type;
226 if (propType == TypeMode::INT) {
227 // integer type
228 is_int_series = true;
229 } else if (propType == TypeMode::AUTO_DETECT) {
230 // auto type. by default
231 if (prop_value.empty())
232 g_log.warning("For sample log in TimeSeriesProperty and values are given "
233 "by MarixWorkspace, the default data type "
234 "is double.");
235 else {
236 // find out the time series data type by prop_value. integer or double
237 int intVal;
238 if (Strings::convert(prop_value, intVal)) {
239 is_int_series = true;
240 }
241 }
242 } else if (propType != TypeMode::DOUBLE) {
243 // unsupported type: anything but double, integer or auto
244 g_log.error() << "TimeSeriesProperty with data type " << prop_number_type << " is not supported.\n";
245 throw std::runtime_error("Unsupported TimeSeriesProperty type.");
246 }
247
248 // check using workspace or some specified start value
249 std::string tsp_ws_name = getPropertyValue("TimeSeriesWorkspace");
250 bool use_ws = !tsp_ws_name.empty();
251 bool use_single_value = !prop_value.empty();
252 if (use_ws && use_single_value) {
253 throw std::runtime_error("Both TimeSeries workspace and sing value are "
254 "specified. It is not allowed.");
255 } else if (!use_ws && !use_single_value) {
256 throw std::runtime_error("Neither TimeSeries workspace or sing value are "
257 "specified. It is not allowed.");
258 }
259
260 // create workspace
261 // get run start
262 Types::Core::DateAndTime startTime = getRunStart(run_obj);
263
264 // initialze the TimeSeriesProperty and add unit
265 if (is_int_series) {
266 auto tsp = std::make_unique<TimeSeriesProperty<int>>(prop_name);
267 if (use_single_value) {
268 int intVal;
269 if (Strings::convert(prop_value, intVal)) {
270 tsp->addValue(startTime, intVal);
271 } else {
272 throw std::invalid_argument("Input value cannot be converted to an integer value.");
273 }
274 }
275 run_obj.addLogData(std::move(tsp));
276 } else {
277 auto tsp = std::make_unique<TimeSeriesProperty<double>>(prop_name);
278 if (use_single_value) {
279 double dblVal;
280 if (Strings::convert(prop_value, dblVal)) {
281 tsp->addValue(startTime, dblVal);
282 } else {
283 throw std::invalid_argument("Input value cannot be converted to a double number.");
284 }
285 }
286 run_obj.addLogData(std::move(tsp));
287 }
288 // add unit
289 run_obj.getProperty(prop_name)->setUnits(prop_unit);
290
291 if (use_ws)
292 setTimeSeriesData(run_obj, prop_name, is_int_series);
293}
294
301void AddSampleLog::setTimeSeriesData(const Run &run_obj, const std::string &property_name, bool value_is_int) {
302 // get input and
303 MatrixWorkspace_sptr data_ws = getProperty("TimeSeriesWorkspace");
304 int ws_index = getProperty("WorkspaceIndex");
305 if (ws_index < 0 || ws_index > static_cast<int>(data_ws->getNumberHistograms()))
306 throw std::runtime_error("Input workspace index is out of range");
307
308 // get meta data
309 bool epochtime(false);
310 std::string timeunit;
311 getMetaData(data_ws, epochtime, timeunit);
312 bool is_second = timeunit == "Second";
313
314 // convert the data in workspace to time series property value
315 std::vector<DateAndTime> time_vec = getTimes(data_ws, ws_index, epochtime, is_second, run_obj);
316 if (value_is_int) {
317 // integer property
318 auto *int_prop = dynamic_cast<TimeSeriesProperty<int> *>(run_obj.getProperty(property_name));
319 std::vector<int> value_vec = getIntValues(data_ws, ws_index);
320 int_prop->addValues(time_vec, value_vec);
321 } else {
322 // double property
323 auto *int_prop = dynamic_cast<TimeSeriesProperty<double> *>(run_obj.getProperty(property_name));
324 std::vector<double> value_vec = getDblValues(data_ws, ws_index);
325 int_prop->addValues(time_vec, value_vec);
326 }
327
328 return;
329}
330
341std::vector<Types::Core::DateAndTime> AddSampleLog::getTimes(const API::MatrixWorkspace_const_sptr &dataws,
342 int workspace_index, bool is_epoch, bool is_second,
343 const API::Run &run_obj) {
344 // get run start time
345 int64_t timeshift(0);
346 if (!is_epoch) {
347 // get the run start time
348 Types::Core::DateAndTime run_start_time = getRunStart(run_obj);
349 timeshift = run_start_time.totalNanoseconds();
350 }
351
352 // set up the time vector
353 std::vector<Types::Core::DateAndTime> timevec;
354 size_t vecsize = dataws->readX(workspace_index).size();
355 for (size_t i = 0; i < vecsize; ++i) {
356 double timedbl = dataws->readX(workspace_index)[i];
357 if (is_second)
358 timedbl *= 1.E9;
359 auto entry_i64 = static_cast<int64_t>(timedbl);
360 Types::Core::DateAndTime entry(timeshift + entry_i64);
361 timevec.emplace_back(entry);
362 }
363
364 return timevec;
365}
366
372Types::Core::DateAndTime AddSampleLog::getRunStart(const API::Run &run_obj) {
373 // TODO/ISSUE/NOW - data ws should be the target workspace with run_start or
374 // proton_charge property!
375 Types::Core::DateAndTime runstart(0);
376 try {
377 runstart = run_obj.startTime();
378 } catch (const std::runtime_error &) {
379 // Swallow the error - startTime will just be 0
380 }
381
382 return runstart;
383}
384
392std::vector<double> AddSampleLog::getDblValues(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index) {
393 std::vector<double> valuevec;
394 size_t vecsize = dataws->readY(workspace_index).size();
395 for (size_t i = 0; i < vecsize; ++i)
396 valuevec.emplace_back(dataws->readY(workspace_index)[i]);
397
398 return valuevec;
399}
400
408std::vector<int> AddSampleLog::getIntValues(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index) {
409 std::vector<int> valuevec;
410 size_t vecsize = dataws->readY(workspace_index).size();
411 for (size_t i = 0; i < vecsize; ++i)
412 valuevec.emplace_back(static_cast<int>(dataws->readY(workspace_index)[i]));
413
414 return valuevec;
415}
416
423void AddSampleLog::getMetaData(const API::MatrixWorkspace_const_sptr &dataws, bool &epochtime, std::string &timeunit) {
424 bool auto_meta = getProperty("AutoMetaData");
425 if (auto_meta) {
426 // get the meta data from the input workspace
427 std::string epochtimestr = dataws->run().getProperty("IsEpochTime")->value();
428 epochtime = epochtimestr == "true";
429 timeunit = dataws->run().getProperty("TimeUnit")->value();
430 } else {
431 // get the meta data from input
432 epochtime = !getProperty("RelativeTime");
433 timeunit = getPropertyValue("TimeUnit");
434 }
435
436 return;
437}
438
439} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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
This class is shared by a few Workspace types and holds information related to a particular experimen...
void addLogData(Kernel::Property *p)
Add a log entry.
Definition LogManager.h:127
bool hasProperty(const std::string &name) const
Does the property exist on the object.
const Types::Core::DateAndTime startTime() const
Return the run start time.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
void removeLogData(const std::string &name, const bool delproperty=true)
Remove a named log entry.
Definition LogManager.h:152
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
A property class for workspaces.
void addSingleValueProperty(API::Run &theRun, const std::string &propName, const std::string &propValue, const std::string &propUnit, const std::string &propNumberType)
Add a single value property.
std::vector< Types::Core::DateAndTime > getTimes(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index, bool is_epoch, bool is_second, const API::Run &run_obj)
get the vector of times of the TimeSeriesProperty entries
void exec() override
Execution code.
void init() override
Initialisation code.
void addStringLog(API::Run &theRun, const std::string &propName, const std::string &propValue, const std::string &propUnit)
Add a sample log (property) with value as string.
std::vector< double > getDblValues(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index)
get value vector of the double TimeSeriesProperty entries
void getMetaData(const API::MatrixWorkspace_const_sptr &dataws, bool &epochtime, std::string &timeunit)
get meta data from input workspace or user input
Types::Core::DateAndTime getRunStart(const API::Run &run_obj)
get run start time
std::vector< int > getIntValues(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index)
get value vector of the integer TimeSeriesProperty entries
void setTimeSeriesData(const API::Run &run_obj, const std::string &property_name, bool value_is_int)
set the time series property's entries to the newly added TimeSeriesProperty
void addTimeSeriesProperty(API::Run &run_obj, const std::string &prop_name, const std::string &prop_value, const std::string &prop_unit, const std::string &prop_number_type)
Add a sample log as a TimeSeriesProperty.
A concrete property based on user options of a finite list of strings.
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Validator to check that a property is not left empty.
The concrete, templated class for properties.
virtual void setUnits(const std::string &unit)
Sets the units of the property, as a string.
Definition Property.cpp:198
A specialised Property class for holding a series of time-value pairs.
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< ExperimentInfo > ExperimentInfo_sptr
Shared pointer to ExperimentInfo.
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition Strings.cpp:696
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53