/
ChangeTimeZero.cpp
360 lines (320 loc) · 12.3 KB
/
ChangeTimeZero.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
#include "MantidAlgorithms/ChangeTimeZero.h"
#include "MantidAlgorithms/CloneWorkspace.h"
#include "MantidAlgorithms/ChangePulsetime.h"
#include "MantidAPI/IMDIterator.h"
#include "MantidAPI/IEventWorkspace.h"
#include "MantidAPI/Run.h"
#include "MantidDataObjects/EventList.h"
#include "MantidKernel/System.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/DateAndTime.h"
#include "MantidKernel/DateTimeValidator.h"
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
namespace Mantid {
namespace Algorithms {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(ChangeTimeZero)
using namespace Mantid::Kernel;
using namespace Mantid::API;
using std::size_t;
namespace {
/**
* General check if we are dealing with a time series
* @param prop :: the property which is being checked
* @return True if the proerpty is a time series, otherwise false.
*/
bool isTimeSeries(Mantid::Kernel::Property *prop) {
auto isTimeSeries = false;
if (dynamic_cast<Mantid::Kernel::ITimeSeriesProperty *>(prop)) {
isTimeSeries = true;
}
return isTimeSeries;
}
}
/** Initialize the algorithm's properties.
*/
void ChangeTimeZero::init() {
declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
"InputWorkspace", "", Direction::Input),
"An input workspace.");
declareProperty<double>("RelativeTimeOffset", m_defaultTimeShift,
"A relative time offset in seconds.");
declareProperty("AbsoluteTimeOffset", m_defaultAbsoluteTimeShift,
"An absolute time offset as an ISO8601 string "
"(YYYY-MM-DDTHH:MM::SS, eg 2013-10-25T13:58:03).");
declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
"OutputWorkspace", "", Direction::Output),
"An output workspace.");
}
/** Execute the algorithm.
*/
void ChangeTimeZero::exec() {
MatrixWorkspace_sptr in_ws = getProperty("InputWorkspace");
// Create a new target workspace if it does not exist
const double progressStartCreateOutputWs = 0.0;
const double progressStopCreateOutputWs = 0.3;
MatrixWorkspace_sptr out_ws = createOutputWS(
in_ws, progressStartCreateOutputWs, progressStopCreateOutputWs);
// Get the time shift in seconds
auto timeShift = getTimeShift(out_ws);
// Set up remaining progress points
const double progressStartShiftTimeLogs = progressStopCreateOutputWs;
double progressStopShiftTimeLogs = progressStartShiftTimeLogs;
if (boost::dynamic_pointer_cast<Mantid::API::IEventWorkspace>(out_ws)) {
progressStopShiftTimeLogs = progressStartShiftTimeLogs + 0.1;
} else {
progressStopShiftTimeLogs = 1.0;
}
const double progressStartShiftNeutrons = progressStopShiftTimeLogs;
const double progressStopShiftNeutrons = 1.0;
// Change the time of the logs.
// Initialize progress reporting.
shiftTimeOfLogs(out_ws, timeShift, progressStartShiftTimeLogs,
progressStopShiftTimeLogs);
// Change the time stamps on the neutrons
shiftTimeOfNeutrons(out_ws, timeShift, progressStartShiftNeutrons,
progressStopShiftNeutrons);
setProperty("OutputWorkspace", out_ws);
}
/**
* Create a new output workspace if required
* @param input :: pointer to an input workspace
* @param startProgress :: start point of the progress
* @param stopProgress :: end point of the progress
* @returns :: pointer to the outputworkspace
*/
API::MatrixWorkspace_sptr
ChangeTimeZero::createOutputWS(API::MatrixWorkspace_sptr input,
double startProgress, double stopProgress) {
MatrixWorkspace_sptr output = getProperty("OutputWorkspace");
// Check whether input == output to see whether a new workspace is required.
if (input != output) {
IAlgorithm_sptr duplicate =
createChildAlgorithm("CloneWorkspace", startProgress, stopProgress);
duplicate->initialize();
duplicate->setProperty<API::Workspace_sptr>(
"InputWorkspace", boost::dynamic_pointer_cast<API::Workspace>(input));
duplicate->execute();
API::Workspace_sptr temp = duplicate->getProperty("OutputWorkspace");
output = boost::dynamic_pointer_cast<API::MatrixWorkspace>(temp);
}
return output;
}
/**
* Get the time shift that was specified by the user. If the time is
* absolute, we need to convert it to relative time.
* @param ws :: a workspace with time stamp information
* @returns A time shift in seconds
*/
double ChangeTimeZero::getTimeShift(API::MatrixWorkspace_sptr ws) const {
auto timeShift = m_defaultTimeShift;
// Check if we are dealing with an absolute time
std::string timeOffset = getProperty("AbsoluteTimeOffset");
if (isAbsoluteTimeShift(timeOffset)) {
DateAndTime desiredTime(timeOffset);
DateAndTime originalTime(getStartTimeFromWorkspace(ws));
timeShift = DateAndTime::secondsFromDuration(desiredTime - originalTime);
} else {
timeShift = getProperty("RelativeTimeOffset");
}
return timeShift;
}
/**
* Change the time of the logs.
* @param ws :: a workspace
* @param timeShift :: the time shift that is applied to the log files
* @param startProgress :: start point of the progress
* @param stopProgress :: end point of the progress
*/
void ChangeTimeZero::shiftTimeOfLogs(Mantid::API::MatrixWorkspace_sptr ws,
double timeShift, double startProgress,
double stopProgress) {
// We need to change the entries for each log which can be:
// 1. any time series: here we change the time values
// 2. string properties: here we change the values if they are ISO8601 times
auto logs = ws->mutableRun().getLogData();
Progress prog(this, startProgress, stopProgress, logs.size());
for (auto &log : logs) {
if (isTimeSeries(log)) {
shiftTimeInLogForTimeSeries(ws, log, timeShift);
} else if (auto stringProperty =
dynamic_cast<PropertyWithValue<std::string> *>(log)) {
shiftTimeOfLogForStringProperty(stringProperty, timeShift);
}
prog.report(name());
}
}
/**
* Shift the time in a time series. This is similar to the implementation in
* @param ws :: a matrix workspace
* @param prop :: a time series log
* @param timeShift :: the time shift in seconds
*/
void ChangeTimeZero::shiftTimeInLogForTimeSeries(
Mantid::API::MatrixWorkspace_sptr ws, Mantid::Kernel::Property *prop,
double timeShift) const {
if (auto timeSeries =
dynamic_cast<Mantid::Kernel::ITimeSeriesProperty *>(prop)) {
auto newlog = timeSeries->cloneWithTimeShift(timeShift);
ws->mutableRun().addProperty(newlog, true);
}
}
/**
* Shift the times in a log of a string property
* @param logEntry :: the string property
* @param timeShift :: the time shift.
*/
void ChangeTimeZero::shiftTimeOfLogForStringProperty(
PropertyWithValue<std::string> *logEntry, double timeShift) const {
// Parse the log entry and replace all ISO8601 strings with an adjusted value
auto value = logEntry->value();
if (checkForDateTime(value)) {
DateAndTime dateTime(value);
DateAndTime shiftedTime = dateTime + timeShift;
logEntry->setValue(shiftedTime.toISO8601String());
}
}
/**
* Shift the time of the neutrons
* @param ws :: a matrix workspace
* @param timeShift :: the time shift in seconds
* @param startProgress :: start point of the progress
* @param stopProgress :: end point of the progress
*/
void ChangeTimeZero::shiftTimeOfNeutrons(Mantid::API::MatrixWorkspace_sptr ws,
double timeShift, double startProgress,
double stopProgress) {
if (auto eventWs =
boost::dynamic_pointer_cast<Mantid::API::IEventWorkspace>(ws)) {
// Use the change pulse time algorithm to change the neutron time stamp
auto alg =
createChildAlgorithm("ChangePulsetime", startProgress, stopProgress);
alg->initialize();
alg->setProperty("InputWorkspace", eventWs);
alg->setProperty("OutputWorkspace", eventWs);
alg->setProperty("TimeOffset", timeShift);
alg->execute();
}
}
/**
* Extract the first good frame of a workspace
* @param ws :: a workspace
* @returns the date and time of the first good frame
*/
DateAndTime
ChangeTimeZero::getStartTimeFromWorkspace(API::MatrixWorkspace_sptr ws) const {
auto run = ws->run();
// Check for the first good frame in the log
Mantid::Kernel::TimeSeriesProperty<double> *goodFrame = nullptr;
try {
goodFrame = run.getTimeSeriesProperty<double>("proton_charge");
} catch (const std::invalid_argument &) {
throw std::invalid_argument("ChangeTimeZero: The log needs a proton_charge "
"time series to determine the zero time.");
}
DateAndTime startTime;
if (goodFrame->size() > 0) {
startTime = goodFrame->firstTime();
}
return startTime;
}
/**
* Check the inputs for invalid values
* @returns A map with validation warnings.
*/
std::map<std::string, std::string> ChangeTimeZero::validateInputs() {
std::map<std::string, std::string> invalidProperties;
// Check the time offset for either a value or a date time
double relativeTimeOffset = getProperty("RelativeTimeOffset");
std::string absoluteTimeOffset = getProperty("AbsoluteTimeOffset");
auto isRelative = isRelativeTimeShift(relativeTimeOffset);
auto absoluteTimeInput = absoluteTimeOffset != m_defaultAbsoluteTimeShift;
auto isAbsolute = isAbsoluteTimeShift(absoluteTimeOffset);
// If both inputs are being used, then return straight away.
if (isRelative && absoluteTimeInput) {
invalidProperties.emplace("RelativeTimeOffset",
"You can either specify a relative time shift or "
"an absolute time shift.");
invalidProperties.emplace("AbsoluteTimeOffset",
"You can either specify a relative time shift or "
"an absolute time shift.");
return invalidProperties;
} else if (!isRelative && !isAbsolute) {
invalidProperties.emplace(
"RelativeTimeOffset",
"TimeOffset must either be a numeric "
"value or a ISO8601 (YYYY-MM-DDTHH:MM::SS) date-time stamp.");
invalidProperties.emplace(
"AbsoluteTimeOffset",
"TimeOffset must either be a numeric "
"value or a ISO8601 (YYYY-MM-DDTHH:MM::SS) date-time stamp.");
}
// If we are dealing with an absolute time we need to ensure that the
// proton_charge entry exists
if (isAbsolute) {
MatrixWorkspace_sptr ws = getProperty("InputWorkspace");
if (ws) {
auto run = ws->run();
try {
run.getTimeSeriesProperty<double>("proton_charge");
} catch (...) {
invalidProperties.emplace(
"InputWorkspace",
"A TimeOffset with an absolute time requires the "
"input workspace to have a proton_charge property in "
"its log.");
}
}
}
return invalidProperties;
}
/** Can the string be transformed to double
* @param val :: value to check
* @return True if the string can be cast to double and otherwise false.
*/
bool ChangeTimeZero::checkForDouble(std::string val) const {
auto isDouble = false;
try {
boost::lexical_cast<double>(val);
isDouble = true;
} catch (boost::bad_lexical_cast const &) {
}
return isDouble;
}
/** Can the string be transformed to a DateTime object
* @param val :: value to check
* @return True if the string can be cast to a DateTime object and otherwise
* false.
*/
bool ChangeTimeZero::checkForDateTime(const std::string &val) const {
auto isDateTime = false;
// Hedge for bad lexical casts in the DateTimeValidator
try {
DateTimeValidator validator = DateTimeValidator();
isDateTime = validator.isValid(val) == "";
} catch (...) {
isDateTime = false;
}
return isDateTime;
}
/**
* Checks if a relative offset has been set
* @param offset :: the offset
* @returns true if the offset has been set
*/
bool ChangeTimeZero::isRelativeTimeShift(double offset) const {
return offset != m_defaultTimeShift;
}
/**
* Checks if an absolute offset has been set
* @param offset :: the offset
* @returns true if the offset has been set
*/
bool ChangeTimeZero::isAbsoluteTimeShift(const std::string &offset) const {
return offset != m_defaultAbsoluteTimeShift && checkForDateTime(offset);
}
} // namespace Mantid
} // namespace Algorithms