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"
19
20#include <string>
21
22namespace {
23
24static const std::string intTypeOption = "Int";
25static const std::string doubleTypeOption = "Double";
26static const std::string autoTypeOption = "AutoDetect";
27
28static const std::string stringLogOption = "String";
29static const std::string numberLogOption = "Number";
30static const std::string numberSeriesLogOption = "Number Series";
31} // namespace
32
33namespace Mantid::Algorithms {
34
35// Register the algorithm into the AlgorithmFactory
36DECLARE_ALGORITHM(AddSampleLog)
37
38using namespace Kernel;
39using namespace API;
40using Types::Core::DateAndTime;
41
43 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace", "", Direction::InOut),
44 "Workspace to add the log entry to");
45 declareProperty("LogName", "", std::make_shared<MandatoryValidator<std::string>>(),
46 "The name that will identify the log entry");
47
48 declareProperty("LogText", "", "The content of the log");
49
50 std::vector<std::string> propOptions;
51 propOptions.emplace_back(stringLogOption);
52 propOptions.emplace_back(numberLogOption);
53 propOptions.emplace_back(numberSeriesLogOption);
54 declareProperty("LogType", stringLogOption, std::make_shared<StringListValidator>(propOptions),
55 "The type that the log data will be.");
56 declareProperty("LogUnit", "", "The units of the log");
57
58 std::vector<std::string> typeOptions;
59 typeOptions.emplace_back(intTypeOption);
60 typeOptions.emplace_back(doubleTypeOption);
61 typeOptions.emplace_back(autoTypeOption);
62 declareProperty("NumberType", autoTypeOption, std::make_shared<StringListValidator>(typeOptions),
63 "Force LogText to be interpreted as a number of type 'int' "
64 "or 'double'.");
65
66 // add optional workspace which contains the data of the TimeSeriesProperty
67 declareProperty(std::make_unique<WorkspaceProperty<API::MatrixWorkspace>>("TimeSeriesWorkspace", "", Direction::Input,
69 "Optional workspace contain the data");
70 declareProperty("WorkspaceIndex", 0, "The workspace index of the TimeSeriesWorkspace to be imported.");
71 declareProperty("AutoMetaData", false,
72 "If it is specified as true, then all the meta data "
73 "information will be retrieved from the input workspace. It "
74 "will be used with algorithm ExportTimeSeriesProperty.");
75
76 std::vector<std::string> time_units{"Second", "Nanosecond"};
77 declareProperty("TimeUnit", "Second", std::make_shared<Kernel::StringListValidator>(time_units),
78 "The unit of the time of the input workspace");
79 declareProperty("RelativeTime", true,
80 "If specified as True, then then the "
81 "time stamps are relative to the run "
82 "start time of the target workspace.");
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 std::string propType = getPropertyValue("LogType");
112 std::string propNumberType = getPropertyValue("NumberType");
113
114 // check inputs
115 if ((propNumberType != autoTypeOption) && ((propType != numberLogOption) && (propType != numberSeriesLogOption))) {
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 == stringLogOption) {
127 // add string log value and return
128 addStringLog(theRun, propName, propValue, propUnit);
129 } else if (propType == numberSeriesLogOption) {
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 return;
143}
144
153void AddSampleLog::addSingleValueProperty(Run &theRun, const std::string &propName, const std::string &propValue,
154 const std::string &propUnit, const std::string &propNumberType) {
155 // add a single value property, integer or double
156 bool value_is_int(false);
157 if (propNumberType != autoTypeOption) {
158 value_is_int = (propNumberType == intTypeOption);
159 } else {
160 int intVal;
161 if (Strings::convert(propValue, intVal)) {
162 value_is_int = true;
163 }
164 }
165
166 // set value
167 if (value_is_int) {
168 // convert to integer
169 int intVal;
170 int convert_to_int = Strings::convert(propValue, intVal);
171 if (convert_to_int == 0) {
172 // spit out error message and set to default value
173 g_log.error() << "Error interpreting string '" << propValue << "' as NumberType Int.";
174 throw std::runtime_error("Invalie integer input");
175 }
176 theRun.addLogData(new PropertyWithValue<int>(propName, intVal));
177 } else {
178 // convert to double
179 double dblVal;
180 int convert_to_dbl = Strings::convert(propValue, dblVal);
181 if (convert_to_dbl == 0) {
182 g_log.error() << "Error interpreting string '" << propValue << "' as NumberType Double.";
183 throw std::runtime_error("Invalid double input.");
184 }
185 theRun.addLogData(new PropertyWithValue<double>(propName, dblVal));
186 g_log.information() << "added property " << propName << " with value " << dblVal << "\n";
187 }
188
189 // add unit
190 theRun.getProperty(propName)->setUnits(propUnit);
191
192 return;
193}
194
202void AddSampleLog::addStringLog(Run &theRun, const std::string &propName, const std::string &propValue,
203 const std::string &propUnit) {
204 theRun.addLogData(new PropertyWithValue<std::string>(propName, propValue));
205 theRun.getProperty(propName)->setUnits(propUnit);
206 return;
207}
208
217void AddSampleLog::addTimeSeriesProperty(Run &run_obj, const std::string &prop_name, const std::string &prop_value,
218 const std::string &prop_unit, const std::string &prop_number_type) {
219 // set up the number type right
220 bool is_int_series(false);
221 if (prop_number_type == intTypeOption) {
222 // integer type
223 is_int_series = true;
224 } else if (prop_number_type == autoTypeOption) {
225 // auto type. by default
226 if (prop_value.empty())
227 g_log.warning("For sample log in TimeSeriesProperty and values are given "
228 "by MarixWorkspace, the default data type "
229 "is double.");
230 else {
231 // find out the time series data type by prop_value. integer or double
232 int intVal;
233 if (Strings::convert(prop_value, intVal)) {
234 is_int_series = true;
235 }
236 }
237 } else if (prop_number_type != doubleTypeOption) {
238 // unsupported type: anything but double, integer or auto
239 g_log.error() << "TimeSeriesProperty with data type " << prop_number_type << " is not supported.\n";
240 throw std::runtime_error("Unsupported TimeSeriesProperty type.");
241 }
242
243 // check using workspace or some specified start value
244 std::string tsp_ws_name = getPropertyValue("TimeSeriesWorkspace");
245 bool use_ws = !tsp_ws_name.empty();
246 bool use_single_value = !prop_value.empty();
247 if (use_ws && use_single_value) {
248 throw std::runtime_error("Both TimeSeries workspace and sing value are "
249 "specified. It is not allowed.");
250 } else if (!use_ws && !use_single_value) {
251 throw std::runtime_error("Neither TimeSeries workspace or sing value are "
252 "specified. It is not allowed.");
253 }
254
255 // create workspace
256 // get run start
257 Types::Core::DateAndTime startTime = getRunStart(run_obj);
258
259 // initialze the TimeSeriesProperty and add unit
260 if (is_int_series) {
261 auto tsp = std::make_unique<TimeSeriesProperty<int>>(prop_name);
262 if (use_single_value) {
263 int intVal;
264 if (Strings::convert(prop_value, intVal)) {
265 tsp->addValue(startTime, intVal);
266 } else {
267 throw std::invalid_argument("Input value cannot be converted to an integer value.");
268 }
269 }
270 run_obj.addLogData(std::move(tsp));
271 } else {
272 auto tsp = std::make_unique<TimeSeriesProperty<double>>(prop_name);
273 if (use_single_value) {
274 double dblVal;
275 if (Strings::convert(prop_value, dblVal)) {
276 tsp->addValue(startTime, dblVal);
277 } else {
278 throw std::invalid_argument("Input value cannot be converted to a double number.");
279 }
280 }
281 run_obj.addLogData(std::move(tsp));
282 }
283 // add unit
284 run_obj.getProperty(prop_name)->setUnits(prop_unit);
285
286 if (use_ws)
287 setTimeSeriesData(run_obj, prop_name, is_int_series);
288}
289
296void AddSampleLog::setTimeSeriesData(Run &run_obj, const std::string &property_name, bool value_is_int) {
297 // get input and
298 MatrixWorkspace_sptr data_ws = getProperty("TimeSeriesWorkspace");
299 int ws_index = getProperty("WorkspaceIndex");
300 if (ws_index < 0 || ws_index > static_cast<int>(data_ws->getNumberHistograms()))
301 throw std::runtime_error("Input workspace index is out of range");
302
303 // get meta data
304 bool epochtime(false);
305 std::string timeunit;
306 getMetaData(data_ws, epochtime, timeunit);
307 bool is_second = timeunit == "Second";
308
309 // convert the data in workspace to time series property value
310 std::vector<DateAndTime> time_vec = getTimes(data_ws, ws_index, epochtime, is_second, run_obj);
311 if (value_is_int) {
312 // integer property
313 auto *int_prop = dynamic_cast<TimeSeriesProperty<int> *>(run_obj.getProperty(property_name));
314 std::vector<int> value_vec = getIntValues(data_ws, ws_index);
315 int_prop->addValues(time_vec, value_vec);
316 } else {
317 // double property
318 auto *int_prop = dynamic_cast<TimeSeriesProperty<double> *>(run_obj.getProperty(property_name));
319 std::vector<double> value_vec = getDblValues(data_ws, ws_index);
320 int_prop->addValues(time_vec, value_vec);
321 }
322
323 return;
324}
325
336std::vector<Types::Core::DateAndTime> AddSampleLog::getTimes(const API::MatrixWorkspace_const_sptr &dataws,
337 int workspace_index, bool is_epoch, bool is_second,
338 API::Run &run_obj) {
339 // get run start time
340 int64_t timeshift(0);
341 if (!is_epoch) {
342 // get the run start time
343 Types::Core::DateAndTime run_start_time = getRunStart(run_obj);
344 timeshift = run_start_time.totalNanoseconds();
345 }
346
347 // set up the time vector
348 std::vector<Types::Core::DateAndTime> timevec;
349 size_t vecsize = dataws->readX(workspace_index).size();
350 for (size_t i = 0; i < vecsize; ++i) {
351 double timedbl = dataws->readX(workspace_index)[i];
352 if (is_second)
353 timedbl *= 1.E9;
354 auto entry_i64 = static_cast<int64_t>(timedbl);
355 Types::Core::DateAndTime entry(timeshift + entry_i64);
356 timevec.emplace_back(entry);
357 }
358
359 return timevec;
360}
361
367Types::Core::DateAndTime AddSampleLog::getRunStart(const API::Run &run_obj) {
368 // TODO/ISSUE/NOW - data ws should be the target workspace with run_start or
369 // proton_charge property!
370 Types::Core::DateAndTime runstart(0);
371 try {
372 runstart = run_obj.startTime();
373 } catch (const std::runtime_error &) {
374 // Swallow the error - startTime will just be 0
375 }
376
377 return runstart;
378}
379
387std::vector<double> AddSampleLog::getDblValues(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index) {
388 std::vector<double> valuevec;
389 size_t vecsize = dataws->readY(workspace_index).size();
390 for (size_t i = 0; i < vecsize; ++i)
391 valuevec.emplace_back(dataws->readY(workspace_index)[i]);
392
393 return valuevec;
394}
395
403std::vector<int> AddSampleLog::getIntValues(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index) {
404 std::vector<int> valuevec;
405 size_t vecsize = dataws->readY(workspace_index).size();
406 for (size_t i = 0; i < vecsize; ++i)
407 valuevec.emplace_back(static_cast<int>(dataws->readY(workspace_index)[i]));
408
409 return valuevec;
410}
411
418void AddSampleLog::getMetaData(const API::MatrixWorkspace_const_sptr &dataws, bool &epochtime, std::string &timeunit) {
419 bool auto_meta = getProperty("AutoMetaData");
420 if (auto_meta) {
421 // get the meta data from the input workspace
422 std::string epochtimestr = dataws->run().getProperty("IsEpochTime")->value();
423 epochtime = epochtimestr == "true";
424 timeunit = dataws->run().getProperty("TimeUnit")->value();
425 } else {
426 // get the meta data from input
427 epochtime = !getProperty("RelativeTime");
428 timeunit = getPropertyValue("TimeUnit");
429 }
430
431 return;
432}
433
434} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
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:115
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Definition: LogManager.cpp:265
const Types::Core::DateAndTime startTime() const
Return the run start time.
Definition: LogManager.cpp:133
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
Definition: LogManager.cpp:404
void removeLogData(const std::string &name, const bool delproperty=true)
Remove a named log entry.
Definition: LogManager.h:140
This class stores information regarding an experimental run as a series of log entries.
Definition: Run.h:38
A property class for workspaces.
std::vector< Types::Core::DateAndTime > getTimes(const API::MatrixWorkspace_const_sptr &dataws, int workspace_index, bool is_epoch, bool is_second, API::Run &run_obj)
get the vector of times of the TimeSeriesProperty entries
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.
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
void setTimeSeriesData(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
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 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.
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
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:186
A specialised Property class for holding a series of time-value pairs.
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
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:665
@ InOut
Both an input & output workspace.
Definition: Property.h:55
@ Input
An input workspace.
Definition: Property.h:53