Mantid
Loading...
Searching...
No Matches
GetTimeSeriesLogInformation.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 +
8#include "MantidAPI/Run.h"
15#include "MantidHistogramData/Histogram.h"
18#include <algorithm>
19#include <fstream>
20
21using namespace Mantid::Kernel;
22using namespace Mantid::API;
23using namespace Mantid::DataObjects;
24using namespace Mantid::HistogramData;
25using Mantid::Types::Core::DateAndTime;
26
27using namespace std;
28
29namespace Mantid::Algorithms {
30
31DECLARE_ALGORITHM(GetTimeSeriesLogInformation)
32
33
36 : API::Algorithm(), API::DeprecatedAlgorithm(), m_dataWS(), mRunStartTime(), mFilterT0(), mFilterTf(),
37 m_intInfoMap(), m_dblInfoMap(), m_log(nullptr), m_timeVec(), m_valueVec(), m_starttime(), m_endtime(),
38 m_ignoreNegativeTime(false) {
39 this->deprecatedDate("2025-11-21");
40}
41
46 std::make_unique<API::WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "Anonymous", Direction::InOut),
47 "Input EventWorkspace. Each spectrum corresponds to 1 pixel");
48
49 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "Dummy", Direction::Output),
50 "Name of the workspace of log delta T distribution. ");
51
52 declareProperty(std::make_unique<WorkspaceProperty<TableWorkspace>>("InformationWorkspace", "", Direction::Output),
53 "Name of optional log statistic information output Tableworkspace.");
54
55 declareProperty("LogName", "", "Log's name to filter events.");
56
57 std::vector<std::string> timeoptions{"Absolute Time (nano second)", "Relative Time (second)"};
58 declareProperty("TimeRangeOption", "Relative Time (second)", std::make_shared<StringListValidator>(timeoptions),
59 "User defined time range (T0, Tf) is of absolute time (second). ");
60
61 declareProperty("FilterStartTime", EMPTY_DBL(),
62 "Earliest time of the events to be selected. "
63 "It can be absolute time (ns), relative time (second) or percentage.");
64 declareProperty("FilterStopTime", EMPTY_DBL(),
65 "Latest time of the events to be selected. "
66 "It can be absolute time (ns), relative time (second) or percentage.");
67
68 declareProperty("TimeStepBinResolution", 0.0001, "Time resolution (second) for time stamp delta T disibution. ");
69
70 declareProperty("IgnoreNegativeTimeInterval", false,
71 "If true, then the time interval with negative number will "
72 "be neglected in doing statistic.");
73}
74
78 // 1. Get wrokspace, log property and etc.
79 m_dataWS = this->getProperty("InputWorkspace");
80 if (!m_dataWS) {
81 throw runtime_error("Inputworkspace cannot be parsed to a MatrixWorkspace.");
82 }
83
84 string logname = getProperty("LogName");
85 if (logname.empty())
86 throw runtime_error("Input log value cannot be an empty string. ");
87
88 Kernel::Property *log = m_dataWS->run().getProperty(logname);
89 if (!log) {
90 stringstream errmsg;
91 errmsg << "Property " << logname << " does not exit in sample of workspace " << m_dataWS->getName() << ".";
92 g_log.error(errmsg.str());
93 throw std::invalid_argument(errmsg.str());
94 } else {
95 m_log = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(log);
96 if (!m_log) {
97 stringstream errmsg;
98 errmsg << "Log " << logname << " is found, but is not a double type time series log";
99 g_log.error(errmsg.str());
100 throw std::invalid_argument(errmsg.str());
101 }
102 }
103
106
107 m_starttime = m_dataWS->run().startTime();
108 m_endtime = m_dataWS->run().endTime();
109
110 m_ignoreNegativeTime = getProperty("IgnoreNegativeTimeInterval");
111
112 // 2. Process start time and end time
114
115 // 3. Check time stamps
117
118 // 4. Calculate distribution of delta T
119 double resolution = getProperty("TimeStepBinResolution");
120 Workspace2D_sptr timestatws = calDistributions(m_timeVec, resolution);
121
122 // 5. Check whether the log is alternating
124
125 // 6. Export error log
126 if (false) {
127 double userinputdt = 1 / 240.1;
128 exportErrorLog(m_dataWS, m_timeVec, userinputdt);
129 }
130
131 // -1. Finish set output properties.
133 setProperty("InformationWorkspace", infows);
134
135 this->setProperty("OutputWorkspace", std::dynamic_pointer_cast<MatrixWorkspace>(timestatws));
136
137 // 4. Do more staticts (examine)
138 // std::string outputdir = this->getProperty("OutputDirectory");
139 // examLog(logname, outputdir);
140}
141
146 // Orignal
147 m_intInfoMap.emplace("Items", m_log->size());
148
149 // Input time
150 double t0r = this->getProperty("FilterStartTime");
151 double tfr = this->getProperty("FilterStopTime");
152
153 // Time unit option
154 string timeoption = this->getProperty("TimeRangeOption");
155 int timecase = 0;
156 if (timeoption == "Absolute Time (nano second)")
157 timecase = 1;
158 else if (timeoption == "Relative Time (second)")
159 timecase = 0;
160 else
161 timecase = -1;
162
163 double duration = static_cast<double>(m_timeVec.back().totalNanoseconds() - m_timeVec[0].totalNanoseconds()) * 1.0E-9;
164
165 // Process start time
166 if (t0r == EMPTY_DBL()) {
167 // Default
168 mFilterT0 = m_timeVec[0];
169 } else {
170 switch (timecase) {
171 case 0:
172 // Relative time (second)
174 break;
175 case 1:
176 // Absolute time (nano second)
178 break;
179 case -1:
180 mFilterT0 = calculateRelativeTime(t0r * duration);
181 break;
182 default:
183 throw runtime_error("Coding error!");
184 break;
185 }
186 } // Filter start time
187
188 // Process filter end time
189 if (tfr == EMPTY_DBL()) {
190 // Default
192 } else {
193 switch (timecase) {
194 // Set with input
195 case 0:
196 // Relative time (second)
198 break;
199 case 1:
200 // Absolute time (nano second)
202 break;
203 case -1:
204 mFilterTf = calculateRelativeTime(tfr * duration);
205 break;
206 default:
207 throw runtime_error("Coding error!");
208 break;
209 }
210 }
211
212 // Check validity
213 if (mFilterTf.totalNanoseconds() <= mFilterT0.totalNanoseconds()) {
214 stringstream errmsg;
215 errmsg << "User defined filter starting time @ " << mFilterT0 << " (T = " << t0r
216 << ") is later than or equal to filer ending time @ " << mFilterTf << " (T = " << tfr << ").";
217 g_log.error(errmsg.str());
218 throw std::invalid_argument(errmsg.str());
219 }
220}
221
226Types::Core::DateAndTime GetTimeSeriesLogInformation::getAbsoluteTime(double abstimens) {
227 DateAndTime temptime(static_cast<int64_t>(abstimens));
228
229 return temptime;
230}
231
235Types::Core::DateAndTime GetTimeSeriesLogInformation::calculateRelativeTime(double deltatime) {
236 int64_t totaltime = m_starttime.totalNanoseconds() + static_cast<int64_t>(deltatime * 1.0E9);
237 DateAndTime abstime(totaltime);
238
239 return abstime;
240}
241
245 auto tablews = std::make_shared<TableWorkspace>();
246
247 tablews->addColumn("str", "Name");
248 tablews->addColumn("double", "Value");
249
250 // 1. Integer part
251 for (const auto &intmapiter : m_intInfoMap) {
252 string text = intmapiter.first;
253 size_t value = intmapiter.second;
254
255 TableRow newrow = tablews->appendRow();
256 newrow << text << static_cast<double>(value);
257 }
258
259 // 2. Double part
260 map<string, double>::iterator dblmapiter;
261 for (dblmapiter = m_dblInfoMap.begin(); dblmapiter != m_dblInfoMap.end(); ++dblmapiter) {
262 string text = dblmapiter->first;
263 double value = dblmapiter->second;
264
265 TableRow newrow = tablews->appendRow();
266 newrow << text << value;
267 }
268
269 return tablews;
270}
271
280void GetTimeSeriesLogInformation::exportErrorLog(const MatrixWorkspace_sptr &ws, const vector<DateAndTime> &abstimevec,
281 double dts) {
282 std::string outputdir = getProperty("OutputDirectory");
283 if (!outputdir.empty() && outputdir.back() != '/')
284 outputdir += "/";
285
286 std::string ofilename = outputdir + "errordeltatime.txt";
287 g_log.notice() << ofilename << '\n';
288 std::ofstream ofs;
289 ofs.open(ofilename.c_str(), std::ios::out);
290
291 Types::Core::DateAndTime t0(ws->run().getProperty("run_start")->value());
292
293 for (size_t i = 1; i < abstimevec.size(); i++) {
294 double tempdts =
295 static_cast<double>(abstimevec[i].totalNanoseconds() - abstimevec[i - 1].totalNanoseconds()) * 1.0E-9;
296 double dev = (tempdts - dts) / dts;
297
298 if (fabs(dev) > 0.5) {
299 double deltapulsetimeSec1 =
300 static_cast<double>(abstimevec[i - 1].totalNanoseconds() - t0.totalNanoseconds()) * 1.0E-9;
301 double deltapulsetimeSec2 =
302 static_cast<double>(abstimevec[i].totalNanoseconds() - t0.totalNanoseconds()) * 1.0E-9;
303 auto index1 = static_cast<int>(deltapulsetimeSec1 * 60);
304 auto index2 = static_cast<int>(deltapulsetimeSec2 * 60);
305
306 ofs << "Error d(T) = " << tempdts << " vs Correct d(T) = " << dts << '\n';
307 ofs << index1 << "\t\t" << abstimevec[i - 1].totalNanoseconds() << "\t\t" << index2 << "\t\t"
308 << abstimevec[i].totalNanoseconds() << '\n';
309 }
310 }
311
312 ofs.close();
313}
314
321Workspace2D_sptr GetTimeSeriesLogInformation::calDistributions(std::vector<Types::Core::DateAndTime> timevec,
322 double stepsize) {
323 // 1. Get a vector of delta T (in unit of seconds)
324 double dtmin = static_cast<double>(timevec.back().totalNanoseconds() - timevec[0].totalNanoseconds()) * 1.0E-9;
325 double dtmax = 0.0;
326
327 vector<double> vecdt(timevec.size() - 1, 0.0);
328 for (size_t i = 1; i < timevec.size(); ++i) {
329 vecdt[i - 1] = static_cast<double>(timevec[i].totalNanoseconds() - timevec[i - 1].totalNanoseconds()) * 1.0E-9;
330 if (vecdt[i - 1] < dtmin)
331 dtmin = vecdt[i - 1];
332 else if (vecdt[i - 1] > dtmax)
333 dtmax = vecdt[i - 1];
334 }
335
336 // 2. Create a vector of counts
337 size_t numbins;
338 if (m_ignoreNegativeTime && dtmin < 0) {
339 numbins = static_cast<size_t>(ceil((dtmax) / stepsize)) + 2;
340 } else {
341 numbins = static_cast<size_t>(ceil((dtmax - dtmin) / stepsize)) + 2;
342 }
343
344 g_log.notice() << "Distribution has " << numbins << " bins. Delta T = (" << dtmin << ", " << dtmax << ")\n";
345
346 Workspace2D_sptr distws = create<Workspace2D>(1, Points(numbins));
347 auto &vecDeltaT = distws->mutableX(0);
348 auto &vecCount = distws->mutableY(0);
349
350 double countmin = dtmin;
351 if (m_ignoreNegativeTime && dtmin < 0)
352 countmin = 0;
353
354 for (size_t i = 0; i < numbins; ++i)
355 vecDeltaT[i] = countmin + (static_cast<double>(i) - 1) * stepsize;
356 for (size_t i = 0; i < numbins; ++i)
357 vecCount[i] = 0;
358
359 // 3. Count
360 for (double dt : vecdt) {
361 int index;
362 if (dt < 0 && m_ignoreNegativeTime) {
363 index = 0;
364 } else {
365 auto viter = lower_bound(vecDeltaT.begin(), vecDeltaT.end(), dt);
366 index = static_cast<int>(viter - vecDeltaT.begin());
367 if (index >= static_cast<int>(vecDeltaT.size())) {
368 // Out of upper boundary
369 g_log.error() << "Find index = " << index << " > vecX.size = " << vecDeltaT.size() << ".\n";
370 } else if (dt < vecDeltaT[index]) {
371 --index;
372 }
373
374 if (index < 0)
375 throw runtime_error("How can this happen.");
376 }
377 vecCount[index] += 1;
378 }
379
380 return distws;
381}
382
386 // 1. Time correctness: same time, disordered time
387 size_t countsame = 0;
388 size_t countinverse = 0;
389 for (size_t i = 1; i < m_timeVec.size(); i++) {
390 Types::Core::DateAndTime tprev = m_timeVec[i - 1];
391 Types::Core::DateAndTime tpres = m_timeVec[i];
392 if (tprev == tpres)
393 countsame++;
394 else if (tprev > tpres)
395 countinverse++;
396 }
397
398 // Written to summary map
399 /*
400 Types::Core::time_duration dts = m_timeVec[0]-m_starttime;
401 Types::Core::time_duration dtf = m_timeVec.back() - m_timeVec[0];
402 size_t f = m_timeVec.size()-1;
403 */
404
405 m_intInfoMap.emplace("Number of Time Stamps", m_timeVec.size());
406 m_intInfoMap.emplace("Number of Equal Time Stamps", countsame);
407 m_intInfoMap.emplace("Number of Reversed Time Stamps", countinverse);
408
409 // 2. Average and standard deviation (delta t)
410 double runduration_sec = static_cast<double>(m_endtime.totalNanoseconds() - m_starttime.totalNanoseconds()) * 1.0E-9;
411 if (runduration_sec < 0.0) {
412 g_log.warning() << "It shows that the run duration is not right. "
413 << "Run start = " << m_starttime.toFormattedString() << "; "
414 << "Run End = " << m_endtime.toFormattedString() << ".\n";
415 g_log.notice() << "Log start time = " << m_timeVec[0].toFormattedString() << "; "
416 << "Log end time = " << m_timeVec.back().toFormattedString() << ".\n";
417 }
418
419 double sum_deltaT1 = 0.0; // sum(dt ) in second
420 double sum_deltaT2 = 0.0; // sum(dt^2) in second^2
421 double max_dt = 0;
422 double min_dt = 0;
423 if (runduration_sec > 0)
424 min_dt = runduration_sec;
425
426 size_t numpts = m_timeVec.size();
427 for (size_t i = 0; i < numpts - 1; ++i) {
428 int64_t dtns = m_timeVec[i + 1].totalNanoseconds() - m_timeVec[i].totalNanoseconds();
429 double dt = static_cast<double>(dtns) * 1.0E-9; // in second
430
431 if (dt < 0) {
432 g_log.warning() << "Reversed dT: dt = " << dt << " between " << m_timeVec[i].toFormattedString() << " and "
433 << m_timeVec[i + 1].toFormattedString() << " @ index = " << i << ".\n";
434 }
435
436 sum_deltaT1 += dt;
437 sum_deltaT2 += dt * dt;
438
439 if (dt > max_dt)
440 max_dt = dt;
441 if (dt < min_dt)
442 min_dt = dt;
443 }
444
445 double avg_dt = sum_deltaT1 / static_cast<double>(numpts - 1);
446 double std_dt = sqrt(sum_deltaT2 / static_cast<double>(numpts - 1) - avg_dt * avg_dt);
447
448 m_dblInfoMap.emplace("Average(dT)", avg_dt);
449 m_dblInfoMap.emplace("Sigma(dt)", std_dt);
450 m_dblInfoMap.emplace("Min(dT)", min_dt);
451 m_dblInfoMap.emplace("Max(dT)", max_dt);
452
453 // 3. Count number of time intervals beyond 10% of deviation
454 /* Temporarily disabled
455 for (size_t i = 1; ; i ++)
456 {
457 int64_t dtns =
458 m_timeVec[i].totalNanoseconds()-m_timeVec[i-1].totalNanoseconds();
459 double dtms = static_cast<double>(dtns)*1.0E-3;
460
461 if (dtms > dtmsA10p)
462 numdtabove10p ++;
463 else if (dtms < dtmsB10p)
464 numdtbelow10p ++;
465
466 } // ENDFOR
467 */
468
469 // 4. Output
470 /* Temporily disabled
471 g_log.notice() << "Run Start = " << t0.totalNanoseconds() << '\n';
472 g_log.notice() << "First Log: " << "Absolute Time = " <<
473 m_timeVec[0].totalNanoseconds() << "(ns), "
474 << "Relative Time = " <<
475 DateAndTime::nanosecondsFromDuration(dts) << "(ns) \n";
476 g_log.notice() << "Last Log: " << "Absolute Time = " <<
477 m_timeVec[f].totalNanoseconds() << "(ns), "
478 << "Relative Time = " <<
479 DateAndTime::nanosecondsFromDuration(dtf) << "(ns) \n";
480 g_log.notice() << "Normal dt = " << numnormal << '\n';
481 g_log.notice() << "Zero dt = " << numsame << '\n';
482 g_log.notice() << "Negative dt = " << numinvert << '\n';
483 g_log.notice() << "Avg d(T) = " << dt << " seconds +/- " << stddt << ",
484 Frequency = " << 1.0/dt << '\n';
485 g_log.notice() << "d(T) (unit ms) is in range [" << mindtms << ", " << maxdtms
486 << "]"<< '\n';
487 g_log.notice() << "Number of d(T) 10% larger than average = " <<
488 numdtabove10p << '\n';
489 g_log.notice() << "Number of d(T) 10% smaller than average = " <<
490 numdtbelow10p << '\n';
491
492 g_log.notice() << "Size of timevec = " << m_timeVec.size() << '\n';
493 */
494}
495
504void GetTimeSeriesLogInformation::checkLogValueChanging(const vector<DateAndTime> &timevec,
505 const vector<double> &values, double delta) {
506 std::stringstream ss;
507 ss << "Alternating Threashold = " << delta << '\n';
508
509 size_t numchange = 0;
510 for (size_t i = 1; i < values.size(); i++) {
511 double tempdelta = values[i] - values[i - 1];
512 if (fabs(tempdelta) < delta) {
513 // Value are 'same'
514 numchange++;
515
516 // An error message
517 ss << "@ " << i << "\tDelta = " << tempdelta << "\t\tTime From " << timevec[i - 1].totalNanoseconds() << " to "
518 << timevec[i].totalNanoseconds() << '\n';
519 }
520 }
521
522 m_intInfoMap.insert(make_pair("Number of adjacent time stamp w/o value change", numchange));
523 g_log.debug() << ss.str();
524}
525
526} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
#define fabs(x)
Definition Matrix.cpp:22
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
Class for marking algorithms as deprecated.
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
GetTimeSeriesLogInformation : Read a TimeSeries log and return some information required by users.
void processTimeRange()
Do statistic on user proposed range and examine the log inside the given time range.
Types::Core::DateAndTime calculateRelativeTime(double deltatime)
Calculate the time from a given relative time from run start.
std::vector< Types::Core::DateAndTime > m_timeVec
Types::Core::DateAndTime getAbsoluteTime(double abstimens)
Convert a value in nanosecond to DateAndTime.
void checkLogValueChanging(const std::vector< Types::Core::DateAndTime > &timevec, const std::vector< double > &values, double delta)
Check whether log values are changing from 2 adjacent time stamps.
DataObjects::Workspace2D_sptr calDistributions(std::vector< Types::Core::DateAndTime > timevec, double stepsize)
Calcualte the distribution of delta T in time stamps.
DataObjects::TableWorkspace_sptr generateStatisticTable()
Generate statistic information table workspace.
void exportErrorLog(const API::MatrixWorkspace_sptr &ws, const std::vector< Types::Core::DateAndTime > &abstimevec, double dts)
Export time stamps looking erroreous.
void checkLogBasicInforamtion()
Check log in workspace including ... ...
void init() override
Definition of all input arguments.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
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 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
Base class for properties.
Definition Property.h:94
A specialised Property class for holding a series of time-value pairs.
int size() const override
Returns the number of values at UNIQUE time intervals in the time series.
std::vector< TYPE > valuesAsVector() const
Return the time series's values (unfiltered) as a vector<TYPE>
std::vector< Types::Core::DateAndTime > timesAsVector() const override
Return the time series's times as a vector<DateAndTime>
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
STL namespace.
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Output
An output workspace.
Definition Property.h:54