Mantid
Loading...
Searching...
No Matches
GenerateEventsFilter.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 +
9#include "MantidAPI/Run.h"
10#include "MantidAPI/TableRow.h"
15#include "MantidHistogramData/Histogram.h"
19
20#include <boost/math/special_functions/round.hpp>
21#include <utility>
22
23using namespace Mantid;
24using namespace Mantid::Kernel;
25using namespace Mantid::API;
26using namespace Mantid::DataObjects;
27using namespace Mantid::HistogramData;
28using Types::Core::DateAndTime;
29using Types::Core::time_duration;
30
31using namespace std;
32
33namespace Mantid::Algorithms {
34DECLARE_ALGORITHM(GenerateEventsFilter)
35
36
39 : API::Algorithm(), m_dataWS(), m_splitWS(), m_filterWS(), m_filterInfoWS(), m_startTime(), m_stopTime(),
40 m_runEndTime(), m_timeUnitConvertFactorToNS(0.), m_dblLog(nullptr), m_intLog(nullptr), m_logAtCentre(false),
41 m_logTimeTolerance(0.), m_forFastLog(false), m_splitters(), m_vecSplitterTime(), m_vecSplitterGroup(),
42 m_useParallel(false), m_vecSplitterTimeSet(), m_vecGroupIndexSet() {}
43
47 // Input/Output Workspaces
49 std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>("InputWorkspace", "", Direction::Input),
50 "An input Matrix workspace.");
51
52 declareProperty(std::make_unique<API::WorkspaceProperty<API::Workspace>>("OutputWorkspace", "", Direction::Output),
53 "The name to use for the output SplittersWorkspace object, ie the filter.");
54
56 std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>("InformationWorkspace", "", Direction::Output),
57 "Optional output for the information of each splitter workspace index");
58
59 declareProperty("FastLog", false, "Fast log will make output workspace to be a matrix workspace. ");
60
61 // Time (general) range
62 declareProperty("StartTime", "",
63 "The start time, such that all events before this time are filtered out: it could be \n"
64 "(1) relative time to run start time in unit as specified property 'UnitOfTime'\n"
65 "(2) absolute time\n"
66 "Absolute time takes a string in format as 1990-01-01T00:00:00, while the relative time takes "
67 "a string representing an integer or a floating-point number.");
68
69 declareProperty("StopTime", "",
70 "The stop time, such that all events after this time are filtered out: it could be \n"
71 "(1) relative time to run start time in unit as specified property 'UnitOfTime'\n"
72 "(2) absolute time\n"
73 "Absolute time takes a string in format as 1990-01-01T00:00:00, while the relative time takes "
74 "a string representing an integer or a floating-point number.");
75
76 // Split by time (only) in steps
78 std::make_unique<ArrayProperty<double>>("TimeInterval"),
79 "Array for lengths of time intervals for splitters: \n"
80 "if the array is empty, then there will be one splitter created from StartTime and StopTime; \n"
81 "if the array has one value, then if this value is positive, all splitters will have same time intervals, else "
82 "the time intervals will be exponentially increasing;\n"
83 "if the size of the array is larger than one, then the splitters can have various time interval values.");
84 setPropertySettings("TimeInterval", std::make_unique<VisibleWhenProperty>("LogName", IS_EQUAL_TO, ""));
85
86 std::vector<std::string> timeoptions{"Seconds", "Nanoseconds", "Percent"};
87 declareProperty("UnitOfTime", "Seconds", std::make_shared<Kernel::StringListValidator>(timeoptions),
88 "This option determines the units of options 'StartTime', 'StopTime' and 'DeltaTime'. "
89 "Allowed units are 'Seconds' or 'Nanoseconds', counting from the run start time, "
90 "or a 'Percentage' of the total run time.");
91
92 // Split by log value (only) in steps
94 "LogName", "",
95 "Name of the sample log to use to filter - for example, the pulse charge is recorded in 'ProtonCharge'.");
96
97 declareProperty("MinimumLogValue", EMPTY_DBL(), "Minimum log value for which to keep events.");
98 setPropertySettings("MinimumLogValue", std::make_unique<VisibleWhenProperty>("LogName", IS_NOT_EQUAL_TO, ""));
99
100 declareProperty("MaximumLogValue", EMPTY_DBL(), "Maximum log value for which to keep events.");
101 setPropertySettings("MaximumLogValue", std::make_unique<VisibleWhenProperty>("LogName", IS_NOT_EQUAL_TO, ""));
102
104 "LogValueInterval", EMPTY_DBL(),
105 "Delta of log value to be sliced into from min log value and max log value; if not given, then only value ");
106 setPropertySettings("LogValueInterval", std::make_unique<VisibleWhenProperty>("LogName", IS_NOT_EQUAL_TO, ""));
107
108 std::vector<std::string> filteroptions{"Both", "Increase", "Decrease"};
109 declareProperty("FilterLogValueByChangingDirection", "Both",
110 std::make_shared<Kernel::StringListValidator>(filteroptions),
111 "d(log value)/dt can be positive and negative, they can be put to different splitters\n"
112 "there are 3 options, 'Both', 'Increase' and 'Decrease' corresponding to d(log value)/dt being any "
113 "value, positive only and negative only respectively.");
114 setPropertySettings("FilterLogValueByChangingDirection",
115 std::make_unique<VisibleWhenProperty>("LogName", IS_NOT_EQUAL_TO, ""));
116
117 declareProperty("TimeTolerance", 0.0,
118 "Tolerance, in seconds, for the event times to keep. It is used in the case to filter by single "
119 "value. How TimeTolerance is applied is highly correlated to LogBoundary and PulseFilter. Check the "
120 "help or algorithm documents for details.");
121 setPropertySettings("TimeTolerance", std::make_unique<VisibleWhenProperty>("LogName", IS_NOT_EQUAL_TO, ""));
122
123 vector<string> logboundoptions{"Centre", "Left", "Other"};
124 auto logvalidator = std::make_shared<StringListValidator>(logboundoptions);
125 declareProperty("LogBoundary", "Centre", logvalidator,
126 "How to treat log values as being measured in the centre of time. "
127 "There are three options, 'Centre', 'Left' and 'Other'. "
128 "This value must be set to Left if the sample log is recorded upon changing,"
129 "which applies to most of the sample environment devices in SNS.");
130 setPropertySettings("LogBoundary", std::make_unique<VisibleWhenProperty>("LogName", IS_NOT_EQUAL_TO, ""));
131
133 "LogValueTolerance", EMPTY_DBL(),
134 "Tolerance of the log value to be included in filter, used in the case to filter by multiple values.");
135 setPropertySettings("LogValueTolerance", std::make_unique<VisibleWhenProperty>("LogName", IS_NOT_EQUAL_TO, ""));
136
137 // Output workspaces' title and name
138 declareProperty("TitleOfSplitters", "", "Title of output splitters workspace and information workspace.");
139
140 // Linear or parallel
141 vector<string> processoptions{"Serial", "Parallel"};
142 auto procvalidator = std::make_shared<StringListValidator>(processoptions);
143 declareProperty("UseParallelProcessing", "Serial", procvalidator,
144 "Use multiple cores to generate events filter by log values: \n"
145 "Serial: Use a single core, good for slow log; \n"
146 "Parallel: Use multiple cores, appropriate for fast log. ");
147
148 declareProperty("NumberOfThreads", EMPTY_INT(), "Number of threads forced to use in the parallel mode. ");
149 declareProperty("UseReverseLogarithmic", false, "Use reverse logarithm for the time filtering.");
150}
151
155 // Process input properties
157
158 // Get Time
160
161 double prog = 0.1;
162 progress(prog);
163
164 // Get Log
165 std::string logname = this->getProperty("LogName");
166 if (logname.empty()) {
167 // Set up filters by time only
169 } else {
170 // Set up filters by log value in time range
171 setFilterByLogValue(logname);
172 }
173
174 // Set output workspaces
175 if (m_forFastLog) {
176 if (m_useParallel) {
178 } else {
180 }
181 setProperty("OutputWorkspace", m_filterWS);
182 } else {
184 setProperty("OutputWorkspace", m_splitWS);
185 }
186 setProperty("InformationWorkspace", m_filterInfoWS);
187}
188
189//----------------------------------------------------------------------------------------------
193 // Input data workspace
194 m_dataWS = this->getProperty("InputWorkspace");
195
196 // Output splitter information workspace
197 std::string title = getProperty("TitleOfSplitters");
198 if (title.empty()) {
199 // Using default
200 title = "Splitters";
201 }
202 m_filterInfoWS = std::make_shared<TableWorkspace>();
203 m_filterInfoWS->setTitle(title);
204 m_filterInfoWS->addColumn("int", "workspacegroup");
205 m_filterInfoWS->addColumn("str", "title");
206
207 // Output Splitters workspace: MatrixWorkspace (optioned) will be generated in
208 // last step
209 m_forFastLog = getProperty("FastLog");
210 if (!m_forFastLog) {
211 m_splitWS = std::shared_ptr<DataObjects::SplittersWorkspace>(new DataObjects::SplittersWorkspace());
212 m_splitWS->setTitle(title);
213 }
214
215 string algtype = getPropertyValue("UseParallelProcessing");
216 if (algtype == "Serial")
217 m_useParallel = false;
218 else if (algtype == "Parallel")
219 m_useParallel = true;
220 else
221 throw std::runtime_error("Impossible to have 3rd type other than Serial and Parallel. ");
222
223 // Conflict
224 if (m_useParallel && !m_forFastLog) {
225 g_log.warning("Parallelization is for fast log only. Automatically turn "
226 "FastLog on. ");
227 m_forFastLog = true;
228 }
229}
230
231//----------------------------------------------------------------------------------------------
237 // Get input
238 std::string s_inpt0 = this->getProperty("StartTime");
239 std::string s_inptf = this->getProperty("StopTime");
240
241 // Default
242 bool defaultstart = s_inpt0.empty();
243 bool defaultstop = s_inptf.empty();
244
245 // Determine format
246 bool instringformat = true;
247 if (!defaultstart && s_inpt0.find(':') == std::string::npos) {
248 // StartTime is not empty and does not contain ":": not ISO
249 instringformat = false;
250 } else if (!defaultstop && s_inptf.find(':') == std::string::npos) {
251 // StopTime is not empty and does not contain ":": not ISO format
252 instringformat = false;
253 }
254
255 // Obtain run time range
256 DateAndTime runstarttime = m_dataWS->run().startTime();
257
259
260 // Obtain time unit converter
261 std::string timeunit = this->getProperty("UnitOfTime");
263 if (timeunit == "Seconds") {
264 // (second)
266 } else if (timeunit == "Nanoseconds") {
267 // (nano-seconds)
269 } else if (timeunit == "Percent") {
270 // (percent of total run time)
271 int64_t runtime_ns = m_runEndTime.totalNanoseconds() - runstarttime.totalNanoseconds();
272 auto runtimed_ns = static_cast<double>(runtime_ns);
273 m_timeUnitConvertFactorToNS = 0.01 * runtimed_ns;
274 } else {
275 // (Not defined/supported)
276 stringstream errss;
277 errss << "TimeType " << timeunit << " is not supported.";
278 throw std::runtime_error(errss.str());
279 }
280
281 // Set up start time
282 if (defaultstart) {
283 // Default
284 m_startTime = runstarttime;
285 } else if (instringformat) {
286 // Time is absolute time in ISO format
287 m_startTime = DateAndTime(s_inpt0);
288 } else {
289 // Relative time in double.
290 double inpt0 = std::stod(s_inpt0.c_str());
291 if (inpt0 < 0) {
292 stringstream errss;
293 errss << "Input relative StartTime " << inpt0 << " cannot be negative. ";
294 throw std::invalid_argument(errss.str());
295 }
296 int64_t t0_ns = runstarttime.totalNanoseconds() + static_cast<int64_t>(inpt0 * m_timeUnitConvertFactorToNS);
297 m_startTime = Types::Core::DateAndTime(t0_ns);
298 }
299
300 // Set up run stop time
301 if (defaultstop) {
302 // Default
304 } else if (instringformat) {
305 // Absolute time in ISO format
306 m_stopTime = DateAndTime(s_inptf);
307 } else {
308 // Relative time in double
309 double inptf = std::stod(s_inptf.c_str());
310 int64_t tf_ns = runstarttime.totalNanoseconds() + static_cast<int64_t>(inptf * m_timeUnitConvertFactorToNS);
311 m_stopTime = Types::Core::DateAndTime(tf_ns);
312 }
313
314 // Check start/stop time
315 // if (m_startTime.totalNanoseconds() >= m_stopTime.totalNanoseconds()) {
316 if (m_startTime >= m_stopTime) {
317 stringstream errss;
318 errss << "Input StartTime " << m_startTime.toISO8601String() << " is equal or later than "
319 << "input StopTime " << m_stopTime.toFormattedString();
320 throw runtime_error(errss.str());
321 }
322
323 g_log.information() << "Filter: StartTime = " << m_startTime << ", StopTime = " << m_stopTime
324 << "; Run start = " << runstarttime.toISO8601String()
325 << ", Run stop = " << m_runEndTime.toISO8601String() << "\n";
326}
327
328//----------------------------------------------------------------------------------------------
332 vector<double> vec_timeintervals = this->getProperty("TimeInterval");
333
334 bool singleslot = false;
335 if (vec_timeintervals.empty()) {
336 singleslot = true;
337 } else {
338 // Check that there is at least one non-zero time value/interval
339 if (std::all_of(vec_timeintervals.begin(), vec_timeintervals.end(), [](double i) { return i == 0; }))
340 throw std::invalid_argument("If TimeInterval has one or more values, at "
341 "least one of those values must be non-zero.");
342 }
343
344 // Progress
345 int64_t totaltime = m_stopTime.totalNanoseconds() - m_startTime.totalNanoseconds();
346
347 g_log.information() << "Filter by time: start @ " << m_startTime.totalNanoseconds() << "; "
348 << "stop @ " << m_stopTime.totalNanoseconds() << "\n";
349
350 if (singleslot) {
351 int wsindex = 0;
352
353 // Default and thus just one interval
354 std::stringstream ss;
355 ss << "Time.Interval.From." << m_startTime << ".To." << m_stopTime;
357 } else if (vec_timeintervals.size() == 1) {
358 double timeinterval = vec_timeintervals[0];
359 int64_t timeslot = 0;
360
361 int64_t runStartTime = m_dataWS->run().startTime().totalNanoseconds();
362 bool isLogarithmic = (timeinterval < 0);
363 m_isReverseLogarithmic = this->getProperty("UseReverseLogarithmic");
364
365 if (m_isReverseLogarithmic && !isLogarithmic) {
366 g_log.warning("UseReverseLogarithmic checked but linear time interval provided. Using linear time interval.");
368 }
369
370 if (isLogarithmic && m_startTime.totalNanoseconds() == runStartTime)
371 throw runtime_error("Cannot do logarithmic time interval if the start time is the same as the start of the run.");
372
373 auto deltatime_ns = static_cast<int64_t>(timeinterval * m_timeUnitConvertFactorToNS);
374 double factor = std::fabs(timeinterval);
375
376 int64_t startTime_ns = m_startTime.totalNanoseconds();
377 int64_t endTime_ns = m_stopTime.totalNanoseconds();
378
379 double relativeStartTime_ns = static_cast<double>(startTime_ns - runStartTime);
380 double relativeEndTime_ns = static_cast<double>(endTime_ns - runStartTime);
381
382 int64_t curtime_ns = !m_isReverseLogarithmic ? startTime_ns - runStartTime : endTime_ns - runStartTime;
383
384 int64_t initialReverseLogStep = startTime_ns - runStartTime;
385
386 int totalNumberOfSlices;
387
388 // we compute the total expected number of slices
389 if (isLogarithmic) {
390 // if logarithmic, first an approximation of the value, then the final value depends if reverseLogarithmic is
391 // used, because of the way the last bin is managed
392 double logSize = std::log(relativeEndTime_ns / relativeStartTime_ns) / std::log1p(factor);
394 totalNumberOfSlices = static_cast<int>(std::ceil(logSize));
395 } else {
396 if (logSize < 1) {
397 totalNumberOfSlices = 1;
398 } else {
399 double previousBin = std::pow(1 + factor, std::floor(logSize) - 1);
400
401 // we check if the last bin can fit without being smaller than the previous one
402 if (relativeEndTime_ns - relativeStartTime_ns * previousBin * (1 + factor) >
403 relativeStartTime_ns * previousBin * factor) {
404 // case where it can
405 totalNumberOfSlices = static_cast<int>(std::ceil(logSize));
406 } else {
407 // case where it cannot and is merged with the previous one
408 totalNumberOfSlices = static_cast<int>(std::floor(logSize));
409 }
410 }
411 }
412 } else {
413 // nice linear case
414 totalNumberOfSlices =
415 static_cast<int>((relativeEndTime_ns - relativeStartTime_ns) / static_cast<double>(deltatime_ns));
416 }
417
418 int wsindex = !m_isReverseLogarithmic ? 0 : totalNumberOfSlices - 1;
419
420 while ((!m_isReverseLogarithmic && curtime_ns + runStartTime < endTime_ns) ||
421 (m_isReverseLogarithmic && curtime_ns + runStartTime > startTime_ns)) {
422 // Calculate next time
423 int64_t nexttime_ns; // note that this is the time since the start of the run
424
425 if (isLogarithmic) {
427 int64_t step = initialReverseLogStep + endTime_ns - runStartTime - curtime_ns;
428 nexttime_ns = curtime_ns - static_cast<int64_t>(static_cast<double>(step) * factor);
429 } else
430 nexttime_ns = static_cast<int64_t>(static_cast<double>(curtime_ns) * (1 + factor));
431
432 } else
433 nexttime_ns = curtime_ns + deltatime_ns;
434
435 if (nexttime_ns + runStartTime > m_stopTime.totalNanoseconds())
436 nexttime_ns = m_stopTime.totalNanoseconds() - runStartTime;
437
438 // in the reverseLog case, we make sure that the "last" bin cannot be smaller than the previous one.
439 if (runStartTime + nexttime_ns - (curtime_ns - nexttime_ns) < m_startTime.totalNanoseconds())
440 nexttime_ns = m_startTime.totalNanoseconds() - runStartTime;
441
442 // Create splitter and information
443 Types::Core::DateAndTime t0(std::min(curtime_ns, nexttime_ns) + runStartTime);
444 Types::Core::DateAndTime tf(std::max(curtime_ns, nexttime_ns) + runStartTime);
445 std::stringstream ss;
446 ss << "Time.Interval.From." << t0 << ".to." << tf;
447
448 addNewTimeFilterSplitter(t0, tf, wsindex, ss.str());
449
450 // Update loop variable and progress
451 curtime_ns = nexttime_ns;
452
453 int64_t newtimeslot;
455 wsindex++;
456 newtimeslot = (endTime_ns - (curtime_ns + runStartTime)) * 90 / totaltime;
457 } else {
458 wsindex--;
459 newtimeslot = (curtime_ns + runStartTime - startTime_ns) * 90 / totaltime;
460 }
461
462 if (newtimeslot > timeslot) {
463 // There is change and update progress
464 timeslot = newtimeslot;
465 double prog = 0.1 + double(timeslot) / 100.0;
466 progress(prog);
467 }
468 } // END-WHILE
469
470 } // END-IF-ELSE
471 else {
472 // Explicitly N time intervals with various interval
473
474 // Construct a vector for time intervals in nanosecond
475 size_t numtimeintervals = vec_timeintervals.size();
476 std::vector<int64_t> vec_dtimens(numtimeintervals);
477 for (size_t id = 0; id < numtimeintervals; ++id) {
478 auto deltatime_ns = static_cast<int64_t>(vec_timeintervals[id] * m_timeUnitConvertFactorToNS);
479 vec_dtimens[id] = deltatime_ns;
480 }
481
482 // Build the splitters
483 int64_t timeslot = 0;
484
485 int64_t curtime_ns = m_startTime.totalNanoseconds();
486 int wsindex = 0;
487 while (curtime_ns < m_stopTime.totalNanoseconds()) {
488 for (size_t id = 0; id < numtimeintervals; ++id) {
489 // get next time interval value
490 int64_t deltatime_ns = vec_dtimens[id];
491 // Calculate next.time
492 int64_t nexttime_ns = curtime_ns + deltatime_ns;
493 bool breaklater = false;
494 if (nexttime_ns > m_stopTime.totalNanoseconds()) {
495 nexttime_ns = m_stopTime.totalNanoseconds();
496 breaklater = true;
497 }
498
499 // Create splitter and information
500 Types::Core::DateAndTime t0(curtime_ns);
501 Types::Core::DateAndTime tf(nexttime_ns);
502 std::stringstream ss;
503 ss << "Time.Interval.From." << t0 << ".to." << tf;
504
505 addNewTimeFilterSplitter(t0, tf, wsindex, ss.str());
506
507 // Update loop variable
508 curtime_ns = nexttime_ns;
509 ++wsindex;
510
511 // Update progress
512 int64_t newtimeslot = (curtime_ns - m_startTime.totalNanoseconds()) * 90 / totaltime;
513 if (newtimeslot > timeslot) {
514 // There is change and update progress
515 timeslot = newtimeslot;
516 double prog = 0.1 + double(timeslot) / 100.0;
517 progress(prog);
518 }
519
520 if (breaklater)
521 break;
522 } // END-FOR
523 } // END-WHILE
524 }
525} // namespace Algorithms
526
527//----------------------------------------------------------------------------------------------
531void GenerateEventsFilter::setFilterByLogValue(const std::string &logname) {
532 // Obtain reference of sample log to filter with
533 m_dblLog = dynamic_cast<TimeSeriesProperty<double> *>(m_dataWS->run().getProperty(logname));
534 m_intLog = dynamic_cast<TimeSeriesProperty<int> *>(m_dataWS->run().getProperty(logname));
535 if (!m_dblLog && !m_intLog) {
536 stringstream errmsg;
537 errmsg << "Log " << logname << " does not exist or is not TimeSeriesProperty in double or integer.";
538 throw runtime_error(errmsg.str());
539 }
540
541 // Clear duplicate value and extend to run end
542 if (m_dblLog) {
543 g_log.debug("Attempting to remove duplicates in double series log.");
547 } else {
548 g_log.debug("Attempting to remove duplicates in integer series log.");
551 }
552
553 // Process input properties related to filter with log value
554 double minvalue = this->getProperty("MinimumLogValue");
555 double maxvalue = this->getProperty("MaximumLogValue");
556 double deltaValue = this->getProperty("LogValueInterval");
557
558 // Log value change direction
559 std::string filterdirection = getProperty("FilterLogValueByChangingDirection");
560 bool filterIncrease;
561 bool filterDecrease;
562 if (filterdirection == "Both") {
563 filterIncrease = true;
564 filterDecrease = true;
565 } else if (filterdirection == "Increase") {
566
567 filterIncrease = true;
568 filterDecrease = false;
569 } else {
570 filterIncrease = false;
571 filterDecrease = true;
572 }
573
574 bool toProcessSingleValueFilter = false;
575 if (isEmpty(deltaValue)) {
576 toProcessSingleValueFilter = true;
577 } else if (deltaValue < 0) {
578 throw runtime_error("Delta value cannot be negative.");
579 }
580
581 // Log boundary
582 string logboundary = getProperty("LogBoundary");
583 m_logAtCentre = bool(logboundary == "Centre");
584
585 m_logTimeTolerance = getProperty("TimeTolerance");
586
587 // Generate filters
588 if (m_dblLog) {
589 // Double TimeSeriesProperty log
590 // Process min/max
591 if (minvalue == EMPTY_DBL()) {
592 minvalue = m_dblLog->minValue();
593 }
594 if (maxvalue == EMPTY_DBL()) {
595 maxvalue = m_dblLog->maxValue();
596 }
597
598 if (minvalue > maxvalue) {
599 stringstream errmsg;
600 errmsg << "Fatal Error: Input minimum log value " << minvalue << " is larger than maximum log value " << maxvalue;
601 throw runtime_error(errmsg.str());
602 } else {
603 g_log.debug() << "Filter by log value: min = " << minvalue << ", max = " << maxvalue
604 << ", process single value? = " << toProcessSingleValueFilter << ", delta value = " << deltaValue
605 << "\n";
606 }
607
608 // Filter double value log
609 if (toProcessSingleValueFilter) {
610 // Generate a filter for a single log value
611 processSingleValueFilter(minvalue, maxvalue, filterIncrease, filterDecrease);
612 } else {
613 // Generate filters for a series of log value
614 processMultipleValueFilters(minvalue, deltaValue, maxvalue, filterIncrease, filterDecrease);
615 }
616 } else {
617 // Integer TimeSeriesProperty log
618 // Process min/max allowed value
619 int minvaluei, maxvaluei;
620 if (minvalue == EMPTY_DBL()) {
621 minvaluei = m_intLog->minValue();
622 minvalue = static_cast<double>(minvaluei);
623 } else
624 minvaluei = boost::math::iround(minvalue);
625
626 if (maxvalue == EMPTY_DBL()) {
627 maxvaluei = m_intLog->maxValue();
628 maxvalue = static_cast<double>(maxvaluei);
629 } else
630 maxvaluei = boost::math::iround(maxvalue);
631
632 if (minvalue > maxvalue) {
633 stringstream errmsg;
634 errmsg << "Fatal Error: Input minimum log value " << minvalue << " is larger than maximum log value " << maxvalue;
635 throw runtime_error(errmsg.str());
636 } else {
637 g_log.information() << "Generate event-filter for integer log: min = " << minvaluei << ", "
638 << "max = " << maxvaluei << "\n";
639 }
640
641 // Split along log
642 DateAndTime runendtime = m_dataWS->run().endTime();
643 processIntegerValueFilter(minvaluei, maxvaluei, filterIncrease, filterDecrease, runendtime);
644
645 } // ENDIFELSE: Double/Integer Log
646
647 g_log.information() << "Minimum value = " << minvalue << ", "
648 << "maximum value = " << maxvalue << ".\n";
649}
650
651//----------------------------------------------------------------------------------------------
660void GenerateEventsFilter::processSingleValueFilter(double minvalue, double maxvalue, bool filterincrease,
661 bool filterdecrease) {
662 // Get parameters time-tolerance and log-boundary
663 double timetolerance = this->getProperty("TimeTolerance");
664 auto timetolerance_ns = static_cast<int64_t>(timetolerance * m_timeUnitConvertFactorToNS);
665
666 std::string logboundary = this->getProperty("LogBoundary");
667 transform(logboundary.begin(), logboundary.end(), logboundary.begin(), ::tolower);
668
669 // Generate filter
670 // std::vector<Kernel::SplittingInterval> splitters;
671 int wsindex = 0;
672 makeFilterBySingleValue(minvalue, maxvalue, static_cast<double>(timetolerance_ns) * 1.0E-9, logboundary == "centre",
673 filterincrease, filterdecrease, m_startTime, m_stopTime, wsindex);
674
675 // Create information table workspace
676 if (!m_filterInfoWS)
677 throw runtime_error("m_filterInfoWS has not been initialized.");
678
679 API::TableRow row = m_filterInfoWS->appendRow();
680 std::stringstream ss;
681 ss << "Log." << m_dblLog->name() << ".From." << minvalue << ".To." << maxvalue << ".Value-change-direction:";
682 if (filterincrease && filterdecrease) {
683 ss << "both";
684 } else if (filterincrease) {
685 ss << "increase";
686 } else {
687 ss << "decrease";
688 }
689 row << 0 << ss.str();
690}
691
692//----------------------------------------------------------------------------------------------
702void GenerateEventsFilter::processMultipleValueFilters(double minvalue, double valueinterval, double maxvalue,
703 bool filterincrease, bool filterdecrease) {
704 // Read more input
705 if (valueinterval <= 0)
706 throw std::invalid_argument("Multiple values filter must have LogValueInterval larger than ZERO.");
707 double valuetolerance = this->getProperty("LogValueTolerance");
708
709 if (valuetolerance == EMPTY_DBL())
710 valuetolerance = 0.5 * valueinterval;
711 else if (valuetolerance < 0.0)
712 throw std::runtime_error("LogValueTolerance cannot be less than zero.");
713
714 // Create log value interval (low/up boundary) list and split information
715 // workspace
716 std::map<size_t, int> indexwsindexmap;
717 std::vector<double> logvalueranges;
718 int wsindex = 0;
719 size_t index = 0;
720
721 double curvalue = minvalue;
722 while (curvalue - valuetolerance < maxvalue) {
723 indexwsindexmap.emplace(index, wsindex);
724
725 // Log interval/value boundary
726 double lowbound = curvalue - valuetolerance;
727 double upbound = curvalue + valueinterval - valuetolerance;
728 logvalueranges.emplace_back(lowbound);
729 logvalueranges.emplace_back(upbound);
730
731 // Workgroup information
732 std::stringstream ss;
733 ss << "Log." << m_dblLog->name() << ".From." << lowbound << ".To." << upbound << ".Value-change-direction:";
734 if (filterincrease && filterdecrease) {
735 ss << "both";
736 } else if (filterincrease) {
737 ss << "increase";
738 } else {
739 ss << "decrease";
740 };
741 API::TableRow newrow = m_filterInfoWS->appendRow();
742 newrow << wsindex << ss.str();
743
744 curvalue += valueinterval;
745 wsindex++;
746 ++index;
747 } // ENDWHILE
748
749 // Debug print
750 stringstream dbsplitss;
751 dbsplitss << "Index map size = " << indexwsindexmap.size() << "\n";
752 for (auto const &mit : indexwsindexmap) {
753 dbsplitss << "Index " << mit.first << ": WS-group = " << mit.second << ". Log value range: ["
754 << logvalueranges[mit.first * 2] << ", " << logvalueranges[mit.first * 2 + 1] << ").\n";
755 }
756 g_log.information(dbsplitss.str());
757
758 // Check split interval obtained wehther is with valid size
759 if (logvalueranges.size() < 2) {
760 g_log.warning("There is no log value interval existing.");
761 return;
762 }
763
764 {
765 // Warning information
766 double upperboundinterval0 = logvalueranges[1];
767 double lowerboundlastinterval = logvalueranges[logvalueranges.size() - 2];
768 double minlogvalue = m_dblLog->minValue();
769 double maxlogvalue = m_dblLog->maxValue();
770 if (minlogvalue > upperboundinterval0 || maxlogvalue < lowerboundlastinterval) {
771 g_log.warning() << "User specifies log interval from " << minvalue - valuetolerance << " to "
772 << maxvalue - valuetolerance << " with interval size = " << valueinterval << "; Log "
773 << m_dblLog->name() << " has range " << minlogvalue << " to " << maxlogvalue
774 << ". Therefore some workgroup index may not have any splitter.\n";
775 }
776 }
777
778 // Generate event filters by log value
779 std::string logboundary = this->getProperty("LogBoundary");
780 transform(logboundary.begin(), logboundary.end(), logboundary.begin(), ::tolower);
781
782 if (m_useParallel) {
783 // Make filters in parallel
784 makeMultipleFiltersByValuesParallel(indexwsindexmap, logvalueranges, logboundary == "centre", filterincrease,
785 filterdecrease, m_startTime, m_stopTime);
786 } else {
787 // Make filters in serial
788 makeMultipleFiltersByValues(indexwsindexmap, logvalueranges, logboundary == "centre", filterincrease,
789 filterdecrease, m_startTime, m_stopTime);
790 }
791}
792
793//-----------------------------------------------------------------------------------------------
813void GenerateEventsFilter::makeFilterBySingleValue(double min, double max, double TimeTolerance, bool centre,
814 bool filterIncrease, bool filterDecrease, DateAndTime startTime,
815 Types::Core::DateAndTime stopTime, int wsindex) {
816 // Do nothing if the log is empty.
817 if (m_dblLog->size() == 0) {
818 g_log.warning() << "There is no entry in this property " << this->name() << "\n";
819 return;
820 }
821
822 // Clear splitters in vector format
823 m_vecSplitterGroup.clear();
824 m_vecSplitterTime.clear();
825
826 // Initialize control parameters
827 bool lastGood = false;
828 time_duration tol = DateAndTime::durationFromSeconds(TimeTolerance);
829 int numgood = 0;
830 DateAndTime currT, start, stop;
831
832 size_t progslot = 0;
833 for (int i = 0; i < m_dblLog->size(); i++) {
834 // The new entry
835 currT = m_dblLog->nthTime(i);
836
837 // A good value?
838 bool isGood = identifyLogEntry(i, currT, lastGood, min, max, startTime, stopTime, filterIncrease, filterDecrease);
839 if (isGood)
840 numgood++;
841
842 // Log status (time/value/value changing direciton) is changed
843 if (isGood != lastGood) {
844 // We switched from bad to good or good to bad
845 if (isGood) {
846 // Start of a good section
847 if (centre)
848 start = currT - tol;
849 else
850 start = currT;
851 } else {
852 // End of the good section
853 if (centre) {
854 stop = currT - tol;
855 } else {
856 stop = currT;
857 }
858
859 std::string empty("");
860 addNewTimeFilterSplitter(start, stop, wsindex, empty);
861
862 // Reset the number of good ones, for next time
863 numgood = 0;
864 }
865 lastGood = isGood;
866 }
867
868 // Progress bar..
869 size_t tmpslot = i * 90 / m_dblLog->size();
870 if (tmpslot > progslot) {
871 progslot = tmpslot;
872 double prog = double(progslot) / 100.0 + 0.1;
873 progress(prog);
874 }
875
876 } // ENDFOR
877
878 if (numgood > 0) {
879 // The log ended on "good" so we need to close it using the last time we
880 // found
881 if (centre)
882 stop = currT - tol;
883 else
884 stop = currT;
885
886 std::string empty("");
887 addNewTimeFilterSplitter(start, stop, wsindex, empty);
888 }
889
890 return;
891}
892
893//----------------------------------------------------------------------------------------------
902bool GenerateEventsFilter::identifyLogEntry(const int &index, const Types::Core::DateAndTime &currT,
903 const bool &lastgood, const double &minvalue, const double &maxvalue,
904 const Types::Core::DateAndTime &startT,
905 const Types::Core::DateAndTime &stopT, const bool &filterIncrease,
906 const bool &filterDecrease) {
907 double val = m_dblLog->nthValue(index);
908
909 // Identify by time and value
910 bool isgood = (val >= minvalue && val < maxvalue) && (currT >= startT && currT < stopT);
911
912 // Consider direction: not both (i.e., not increase or not decrease)
913 if (isgood && (!filterIncrease || !filterDecrease)) {
914 int numlogentries = m_dblLog->size();
915 double diff;
916 if (index < numlogentries - 1) {
917 // For a non-last log entry
918 diff = m_dblLog->nthValue(index + 1) - val;
919 } else {
920 // Last log entry: follow the last direction
921 diff = val - m_dblLog->nthValue(index - 1);
922 }
923
924 if (diff > 0 && filterIncrease)
925 isgood = true;
926 else if (diff < 0 && filterDecrease)
927 isgood = true;
928 else if (diff == 0)
929 isgood = lastgood;
930 else
931 isgood = false;
932 }
933
934 return isgood;
935}
936
937//-----------------------------------------------------------------------------------------------
953void GenerateEventsFilter::makeMultipleFiltersByValues(map<size_t, int> indexwsindexmap,
954 const vector<double> &logvalueranges, bool centre,
955 bool filterIncrease, bool filterDecrease, DateAndTime startTime,
956 DateAndTime stopTime) {
957 g_log.notice("Starting method 'makeMultipleFiltersByValues'. ");
958
959 // Return if the log is empty.
960 int logsize = m_dblLog->size();
961 if (logsize == 0) {
962 g_log.warning() << "There is no entry in this property " << m_dblLog->name() << '\n';
963 return;
964 }
965
966 // Set up
967 double timetolerance = 0.0;
968 if (centre) {
969 timetolerance = this->getProperty("TimeTolerance");
970 }
971 time_duration tol = DateAndTime::durationFromSeconds(timetolerance);
972
973 // Determine the number of threads to use
974 // int numThreads = 1;
975 vector<DateAndTime> tempvectimes;
976 // tempvectimes.reserve(m_dblLog->size());
977 vector<int> tempvecgroup;
978 // tempvecgroup.reserve(m_dblLog->size());
979 m_vecSplitterTimeSet.emplace_back(tempvectimes);
980 m_vecGroupIndexSet.emplace_back(tempvecgroup);
981 int istart = 0;
982 auto iend = static_cast<int>(logsize - 1);
983
984 makeMultipleFiltersByValuesPartialLog(istart, iend, m_vecSplitterTime, m_vecSplitterGroup, std::move(indexwsindexmap),
985 logvalueranges, tol, filterIncrease, filterDecrease, startTime, stopTime);
986
987 progress(1.0);
988}
989
990//-----------------------------------------------------------------------------------------------
1007void GenerateEventsFilter::makeMultipleFiltersByValuesParallel(const map<size_t, int> &indexwsindexmap,
1008 const vector<double> &logvalueranges, bool centre,
1009 bool filterIncrease, bool filterDecrease,
1010 DateAndTime startTime, DateAndTime stopTime) {
1011 // Return if the log is empty.
1012 int logsize = m_dblLog->size();
1013 if (logsize == 0) {
1014 g_log.warning() << "There is no entry in this property " << m_dblLog->name() << '\n';
1015 return;
1016 }
1017
1018 // Set up
1019 double timetolerance = 0.0;
1020 if (centre) {
1021 timetolerance = this->getProperty("TimeTolerance");
1022 }
1023 time_duration tol = DateAndTime::durationFromSeconds(timetolerance);
1024
1025 // Determine the number of threads to use
1026 int numThreads = getProperty("NumberOfThreads");
1027 if (isEmpty(numThreads))
1028 numThreads = static_cast<int>(PARALLEL_GET_MAX_THREADS);
1029
1030 // Limit the number of threads.
1031 numThreads = std::min(numThreads, logsize / 8);
1032
1033 // Determine the istart and iend
1034 // For split, log should be [0, n], [n, 2n], [2n, 3n], ... as to look into n
1035 // log values
1036 std::vector<int> vecStart, vecEnd;
1037 int partloglength = (logsize - 1) / numThreads;
1038 int extra = (logsize - 1) % numThreads;
1039 for (int i = 0; i < numThreads; ++i) {
1040 int istart = i * partloglength;
1041 if (i < extra)
1042 istart += i;
1043 else
1044 istart += extra;
1045
1046 int iend = istart + partloglength;
1047 if (i < extra)
1048 ++iend;
1049
1050 vecStart.emplace_back(istart);
1051 vecEnd.emplace_back(iend);
1052 }
1053
1054 {
1055 stringstream dbss;
1056 dbss << "Number of thread = " << numThreads << ", Log Size = " << logsize << "\n";
1057 for (size_t i = 0; i < vecStart.size(); ++i) {
1058 dbss << "Thread " << i << ": Log range: [" << vecStart[i] << ", " << vecEnd[i] << ") "
1059 << "Size = " << vecEnd[i] - vecStart[i] << "\n";
1060 }
1061 g_log.information(dbss.str());
1062 }
1063
1064 // Create partial vectors
1065 m_vecSplitterTimeSet.clear();
1066 m_vecGroupIndexSet.clear();
1067 for (int i = 0; i < numThreads; ++i) {
1068 vector<DateAndTime> tempvectimes;
1069 tempvectimes.reserve(m_dblLog->size());
1070 vector<int> tempvecgroup;
1071 tempvecgroup.reserve(m_dblLog->size());
1072 m_vecSplitterTimeSet.emplace_back(tempvectimes);
1073 m_vecGroupIndexSet.emplace_back(tempvecgroup);
1074 }
1075
1076 // Create event filters/splitters in parallel
1077
1078 PRAGMA_OMP(parallel for schedule(dynamic, 1) )
1079 for (int i = 0; i < numThreads; ++i) {
1081
1082 int istart = vecStart[i];
1083 int iend = vecEnd[i];
1084
1086 indexwsindexmap, logvalueranges, tol, filterIncrease, filterDecrease,
1087 startTime, stopTime);
1089 }
1091
1092 // Concatenate splitters on different threads together
1093 for (int i = 1; i < numThreads; ++i) {
1094 if (m_vecSplitterTimeSet[i - 1].back() == m_vecSplitterTimeSet[i].front()) {
1095 // T_(i).back() = T_(i+1).front()
1096 if (m_vecGroupIndexSet[i - 1].back() == m_vecGroupIndexSet[i].front()) {
1097 // G_(i).back() = G_(i+1).front(), combine these adjacent 2 splitters
1098 // Rule out impossible situation
1099 if (m_vecGroupIndexSet[i - 1].back() == -1) {
1100 // Throw with detailed error message
1101 stringstream errss;
1102 errss << "Previous vector of group index set (" << (i - 1) << ") is equal to -1. "
1103 << "It is not likely to happen! Size of previous vector of "
1104 "group is "
1105 << m_vecGroupIndexSet[i - 1].size() << ". \nSuggest to use sequential mode. ";
1106 throw runtime_error(errss.str());
1107 }
1108
1109 // Pop back last element
1110 m_vecGroupIndexSet[i - 1].pop_back();
1111 DateAndTime newt0 = m_vecSplitterTimeSet[i - 1].front();
1112 m_vecSplitterTimeSet[i - 1].pop_back();
1113 DateAndTime origtime = m_vecSplitterTimeSet[i][0];
1114 m_vecSplitterTimeSet[i][0] = newt0;
1115 g_log.debug() << "Splitter at the end of thread " << i << " is extended from " << origtime << " to " << newt0
1116 << "\n";
1117 } else {
1118 // 2 different and continous spliiter: ideal case without any
1119 // operation
1120 ;
1121 }
1122 } else {
1123 // T_(i).back() != T_(i+1).front(): need to fill the gap in time
1124 int lastindex = m_vecGroupIndexSet[i - 1].back();
1125 int firstindex = m_vecGroupIndexSet[i].front();
1126
1127 if (lastindex != -1 && firstindex != -1) {
1128 // T_stop < T_start, I_stop != -1, I_start != 1. : Insert a minus-one
1129 // entry to make it complete
1130 m_vecGroupIndexSet[i - 1].emplace_back(-1);
1131 m_vecSplitterTimeSet[i - 1].emplace_back(m_vecSplitterTimeSet[i].front());
1132 } else if (lastindex == -1 && m_vecGroupIndexSet[i - 1].size() == 1) {
1133 // Empty splitter of the thread. Extend this to next
1134 m_vecSplitterTimeSet[i - 1].back() = m_vecSplitterTimeSet[i].front();
1135 g_log.debug() << "Thread = " << i << ", change ending time of " << i - 1 << " to "
1136 << m_vecSplitterTimeSet[i].front() << "\n";
1137 } else if (firstindex == -1 && m_vecGroupIndexSet[i].size() == 1) {
1138 // Empty splitter of the thread. Extend last one to this
1139 m_vecSplitterTimeSet[i].front() = m_vecSplitterTimeSet[i - 1].back();
1140 g_log.debug() << "Thread = " << i << ", change starting time to " << m_vecSplitterTimeSet[i].front() << "\n";
1141 } else {
1142 throw runtime_error("It is not possible to have start or end of "
1143 "filter to be minus-one index. ");
1144 }
1145 }
1146 }
1147
1148 progress(1.0);
1149}
1150
1151//----------------------------------------------------------------------------------------------
1155 int istart, int iend, std::vector<Types::Core::DateAndTime> &vecSplitTime, std::vector<int> &vecSplitGroup,
1156 map<size_t, int> indexwsindexmap, const vector<double> &logvalueranges, const time_duration &tol,
1157 bool filterIncrease, bool filterDecrease, DateAndTime startTime, DateAndTime stopTime) {
1158 // Check
1159 int logsize = m_dblLog->size();
1160 if (istart < 0 || iend >= logsize)
1161 throw runtime_error("Input index of makeMultipleFiltersByValuesPartialLog "
1162 "is out of boundary. ");
1163
1164 int64_t tol_ns = tol.total_nanoseconds();
1165
1166 // Define loop control parameters
1167 const Types::Core::DateAndTime ZeroTime(0);
1168 int lastindex = -1;
1169 int currindex;
1170 DateAndTime currTime, start, stop;
1171 // size_t progslot = 0;
1172
1173 g_log.information() << "Log time coverage (index: " << istart << ", " << iend << ") from "
1174 << m_dblLog->nthTime(istart) << ", " << m_dblLog->nthTime(iend) << "\n";
1175
1176 DateAndTime laststoptime(0);
1177 int lastlogindex = m_dblLog->size() - 1;
1178
1179 int prevDirection = determineChangingDirection(istart);
1180
1181 for (int i = istart; i <= iend; i++) {
1182 // Initialize status flags and new entry
1183 bool breakloop = false;
1184 bool createsplitter = false;
1185
1186 currTime = m_dblLog->nthTime(i);
1187 if ((i + 1 < iend) && (currTime == m_dblLog->nthTime(i + 1)))
1188 continue; // skip to the next value
1189 double currValue = m_dblLog->nthValue(i);
1190
1191 // Filter out by time and direction (optional)
1192 if (currTime < startTime) {
1193 // case i. Too early, do nothing
1194 createsplitter = false;
1195 } else if (currTime > stopTime) {
1196 // case ii. Too late. Put to splitter if half of splitter is done. But
1197 // still within range
1198 breakloop = true;
1199 stop = currTime;
1200 if (start.totalNanoseconds() > 0) {
1201 createsplitter = true;
1202 }
1203 }
1204
1205 // Check log within given time range
1206 bool newsplitter = false; // Flag to start a new split in this loop
1207
1208 // Determine direction
1209 int direction = 0;
1210 if (i < lastlogindex) {
1211 // Not the last log entry
1212 double diff = m_dblLog->nthValue(i + 1) - m_dblLog->nthValue(i);
1213 if (diff > 0)
1214 direction = 1;
1215 else if (diff < 0)
1216 direction = -1;
1217 else
1218 direction = prevDirection;
1219 } else {
1220 // Last log entry: use the previous direction
1221 direction = prevDirection;
1222 }
1223 prevDirection = direction;
1224
1225 // Examine log value for filter
1226 // Determine whether direction is fine
1227 bool correctdir = true;
1228 if (filterIncrease && filterDecrease) {
1229 // Both direction is fine
1230 correctdir = true;
1231 } else {
1232 // Filter out one direction
1233 if (filterIncrease && direction > 0)
1234 correctdir = true;
1235 else if (filterDecrease && direction < 0)
1236 correctdir = true;
1237 else if (direction == 0)
1238 throw runtime_error("Direction is not determined.");
1239 else
1240 correctdir = false;
1241 } // END-IF-ELSE: Direction
1242
1243 // Treat the log entry based on: changing direction (+ time range)
1244 if (correctdir) {
1245 // Check this value whether it falls into any range
1246 size_t index = searchValue(logvalueranges, currValue);
1247
1248 bool valueWithinMinMax = true;
1249 if (index > logvalueranges.size()) {
1250 // Out of range
1251 valueWithinMinMax = false;
1252 }
1253
1254 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
1255 stringstream dbss;
1256 dbss << "[DBx257] Examine Log Index " << i << ", Value = " << currValue << ", Data Range Index = " << index
1257 << "; "
1258 << "Group Index = " << indexwsindexmap[index / 2]
1259 << " (log value range vector size = " << logvalueranges.size() << "): ";
1260 if (index == 0)
1261 dbss << logvalueranges[index] << ", " << logvalueranges[index + 1];
1262 else if (index == logvalueranges.size())
1263 dbss << logvalueranges[index - 1] << ", " << logvalueranges[index];
1264 else if (valueWithinMinMax)
1265 dbss << logvalueranges[index - 1] << ", " << logvalueranges[index] << ", " << logvalueranges[index + 1];
1266 g_log.debug(dbss.str());
1267 }
1268
1269 if (valueWithinMinMax) {
1270 if (index % 2 == 0) {
1271 // [Situation] Falls in the interval
1272 currindex = indexwsindexmap[index / 2];
1273
1274 if (currindex != lastindex && start.totalNanoseconds() == 0) {
1275 // Group index is different from last and start is not set up: new
1276 // a region!
1277 newsplitter = true;
1278 } else if (currindex != lastindex && start.totalNanoseconds() > 0) {
1279 // Group index is different from last and start is set up: close
1280 // a region and new a region
1281 stop = currTime;
1282 createsplitter = true;
1283 newsplitter = true;
1284 } else if (currindex == lastindex && start.totalNanoseconds() > 0) {
1285 // Still of the group index
1286 if (i == iend) {
1287 // Last entry in this section of log. Need to flag to close the
1288 // pair
1289 stop = currTime;
1290 createsplitter = true;
1291 newsplitter = false;
1292 } else {
1293 // Do nothing
1294 ;
1295 }
1296 } else {
1297 // An impossible situation
1298 std::stringstream errmsg;
1299 double lastvalue = m_dblLog->nthValue(i - 1);
1300 errmsg << "Impossible to have currindex == lastindex == " << currindex
1301 << ", while start is not init. Log Index = " << i << "\t value = " << currValue
1302 << "\t, Index = " << index << " in range " << logvalueranges[index] << ", "
1303 << logvalueranges[index + 1] << "; Last value = " << lastvalue;
1304 throw std::runtime_error(errmsg.str());
1305 }
1306 } // [In-bound: Inside interval]
1307 else {
1308 // [Situation] Fall between interval (which is not likley happen)
1309 currindex = -1;
1310 g_log.warning() << "Not likely to happen! Current value = " << currValue
1311 << " is within value range but its index = " << index << " has no map to group index. "
1312 << "\n";
1313 if (start.totalNanoseconds() > 0) {
1314 // Close the interval pair if it has been started.
1315 stop = currTime;
1316 createsplitter = true;
1317 }
1318 } // [In-bound: Between interval]
1319 } else {
1320 // Out of a range: check whether there is a splitter started
1321 currindex = -1;
1322 if (start.totalNanoseconds() > 0) {
1323 // End situation
1324 stop = currTime;
1325 createsplitter = true;
1326 }
1327 } // [Out-bound]
1328
1329 } // [CORRECT DIRECTION]
1330 else {
1331 // Log Index i falls out b/c out of wrong direction
1332 currindex = -1;
1333
1334 // Condition to generate a Splitter (close parenthesis)
1335 if (start.totalNanoseconds() > 0) {
1336 stop = currTime;
1337 createsplitter = true;
1338 }
1339 }
1340
1341 // d) Create Splitter
1342 if (createsplitter) {
1343 // make_splitter(start, stop, lastindex, tol);
1344 makeSplitterInVector(vecSplitTime, vecSplitGroup, start, stop, lastindex, tol_ns, laststoptime);
1345
1346 // reset
1347 start = ZeroTime;
1348 }
1349
1350 // e) Start new splitter: have to be here due to start cannot be updated
1351 // before a possible splitter generated
1352 if (newsplitter)
1353 start = currTime;
1354
1355 // f) Break
1356 if (breakloop)
1357 break;
1358
1359 // e) Update loop variable
1360 lastindex = currindex;
1361
1362 } // For each log value
1363
1364 // To fill the blanks at the end of log to make last entry of splitter is stop
1365 // time
1366 // To make it non-empty
1367 if (vecSplitTime.empty()) {
1368 start = m_dblLog->nthTime(istart);
1369 stop = m_dblLog->nthTime(iend);
1370 lastindex = -1;
1371 makeSplitterInVector(vecSplitTime, vecSplitGroup, start, stop, lastindex, tol_ns, laststoptime);
1372 }
1373}
1374
1375//-----------------------------------------------------------------------------------------------
1383void GenerateEventsFilter::processIntegerValueFilter(int minvalue, int maxvalue, bool filterIncrease,
1384 bool filterDecrease, DateAndTime runend) {
1385 // Determine the filter mode and delta
1386 int delta = 0;
1387 bool singlevaluemode;
1388 if (minvalue == maxvalue) {
1389 singlevaluemode = true;
1390 } else {
1391 singlevaluemode = false;
1392
1393 double deltadbl = getProperty("LogValueInterval");
1394 if (isEmpty(deltadbl))
1395 delta = maxvalue - minvalue + 1;
1396 else
1397 delta = boost::math::iround(deltadbl);
1398
1399 if (delta <= 0) {
1400 stringstream errss;
1401 errss << "In a non-single value mode, LogValueInterval cannot be 0 or "
1402 "negative for integer. "
1403 << "Current input is " << deltadbl << ".";
1404 throw runtime_error(errss.str());
1405 } else
1406 g_log.information() << "Generate event-filter by integer log: step = " << delta << "\n";
1407 }
1408
1409 // Search along log to generate splitters
1410 size_t numlogentries = m_intLog->size();
1411 vector<DateAndTime> vecTimes = m_intLog->timesAsVector();
1412 vector<int> vecValue = m_intLog->valuesAsVector();
1413
1414 time_duration timetol = DateAndTime::durationFromSeconds(m_logTimeTolerance * m_timeUnitConvertFactorToNS * 1.0E-9);
1415 int64_t timetolns = timetol.total_nanoseconds();
1416
1417 DateAndTime splitstarttime(0);
1418 int pregroup = -1;
1419 DateAndTime laststoptime(0);
1420
1421 g_log.debug() << "Number of integer log entries = " << numlogentries << ".\n";
1422
1423 for (size_t i = 0; i < numlogentries; ++i) {
1424 int currvalue = vecValue[i];
1425 int currgroup = -1;
1426
1427 // Determine whether this log value is allowed and then the ws group it
1428 // belonged to.
1429 if (currvalue >= minvalue && currvalue <= maxvalue) {
1430 // Log value is in specified range
1431 if ((i == 0) || (filterIncrease && vecValue[i] >= vecValue[i - 1]) ||
1432 (filterDecrease && vecValue[i] <= vecValue[i - 1])) {
1433 // First entry (regardless direction) and other entries considering
1434 // change of value
1435 if (singlevaluemode) {
1436 currgroup = 0;
1437 } else {
1438 currgroup = (currvalue - minvalue) / delta;
1439 }
1440 }
1441 }
1442
1443 // Make a new splitter if condition is met
1444 bool statuschanged;
1445 if (pregroup >= 0 && currgroup < 0) {
1446 // previous log is in allowed region. but this one is not. create a
1447 // splitter
1448 if (splitstarttime.totalNanoseconds() == 0)
1449 throw runtime_error("Programming logic error.");
1450
1451 makeSplitterInVector(m_vecSplitterTime, m_vecSplitterGroup, splitstarttime, vecTimes[i], pregroup, timetolns,
1452 laststoptime);
1453 laststoptime = vecTimes[i];
1454
1455 splitstarttime = DateAndTime(0);
1456 statuschanged = true;
1457 } else if (pregroup < 0 && currgroup >= 0) {
1458 // previous log is not allowed, but this one is. this is the start of a
1459 // new splitter
1460 splitstarttime = vecTimes[i];
1461 statuschanged = true;
1462 } else if (currgroup >= 0 && pregroup != currgroup) {
1463 // migrated to a new region
1464 if (splitstarttime.totalNanoseconds() == 0)
1465 throw runtime_error("Programming logic error (1).");
1466 makeSplitterInVector(m_vecSplitterTime, m_vecSplitterGroup, splitstarttime, vecTimes[i], pregroup, timetolns,
1467 laststoptime);
1468 laststoptime = vecTimes[i];
1469
1470 splitstarttime = vecTimes[i];
1471 statuschanged = true;
1472 } else {
1473 // no need to do anything: status is not changed
1474 statuschanged = false;
1475 }
1476
1477 // Update group/pregroup
1478 if (statuschanged)
1479 pregroup = currgroup;
1480 } // ENDOFLOOP on time series
1481
1482 // Create the last splitter if existing
1483 if (pregroup >= 0) {
1484 // Last entry is in an allowed region.
1485 if (splitstarttime.totalNanoseconds() == 0)
1486 throw runtime_error("Programming logic error (1).");
1487 makeSplitterInVector(m_vecSplitterTime, m_vecSplitterGroup, splitstarttime, runend, pregroup, timetolns,
1488 laststoptime);
1489 }
1490
1491 // Write to the information workspace
1492 // FIXME - Consider to refactor this part with all other methods
1493 if (singlevaluemode) {
1494 TableRow newrow = m_filterInfoWS->appendRow();
1495 stringstream message;
1496 message << m_intLog->name() << " = " << minvalue;
1497 newrow << 0 << message.str();
1498 } else {
1499 int logvalue = minvalue;
1500 int wsindex = 0;
1501 while (logvalue <= maxvalue) {
1502 stringstream message;
1503 if (logvalue + delta - 1 > logvalue)
1504 message << m_intLog->name() << "=[" << logvalue << "," << logvalue + delta - 1 << "]";
1505 else
1506 message << m_intLog->name() << "=" << logvalue;
1507
1508 message << ".Value change direction:";
1509 if (filterIncrease && filterDecrease)
1510 message << "Both";
1511 else if (filterIncrease)
1512 message << "Increasing";
1513 else if (filterDecrease)
1514 message << "Decreasing";
1515
1516 TableRow newrow = m_filterInfoWS->appendRow();
1517 newrow << wsindex << message.str();
1518
1519 ++wsindex;
1520 logvalue += delta;
1521 }
1522 }
1523
1524 g_log.information() << "Integer log " << m_intLog->name() << ": Number of splitters = " << m_vecSplitterGroup.size()
1525 << ", Number of split info = " << m_filterInfoWS->rowCount() << ".\n";
1526}
1527
1528//----------------------------------------------------------------------------------------------
1538size_t GenerateEventsFilter::searchValue(const std::vector<double> &sorteddata, double value) {
1539 // Case of out-of-boundary
1540 size_t numdata = sorteddata.size();
1541 size_t outrange = numdata + 1;
1542 if (numdata == 0)
1543 return outrange;
1544 else if (value < sorteddata.front() || value > sorteddata.back())
1545 return outrange;
1546
1547 // Binary search
1548 size_t index =
1549 static_cast<size_t>(std::lower_bound(sorteddata.begin(), sorteddata.end(), value) - sorteddata.begin());
1550
1551 if (value < sorteddata[index] && index % 2 == 1) {
1552 // value to search is less than the boundary: use the index of the one just
1553 // smaller to the value to search
1554 --index;
1555 } else if (value == sorteddata[index] && index % 2 == 1) {
1556 // value to search is on the boudary, i..e, a,b,b,c,c,....,x,x,y
1557 ++index;
1558
1559 // return if out of range
1560 if (index == sorteddata.size())
1561 return outrange;
1562 }
1563
1564 return index;
1565}
1566
1567//----------------------------------------------------------------------------------------------
1571 int direction = 0;
1572
1573 // Search to earlier entries
1574 int index = startindex;
1575 while (direction == 0 && index > 0) {
1576 double diff = m_dblLog->nthValue(index) - m_dblLog->nthValue(index - 1);
1577 if (diff > 0)
1578 direction = 1;
1579 else if (diff < 0)
1580 direction = -1;
1581
1582 --index;
1583 }
1584
1585 if (direction != 0)
1586 return direction;
1587
1588 // Search to later entries
1589 index = startindex;
1590 int maxindex = m_dblLog->size() - 1;
1591 while (direction == 0 && index < maxindex) {
1592 double diff = m_dblLog->nthValue(index + 1) - m_dblLog->nthValue(index);
1593 if (diff > 0)
1594 direction = 1;
1595 else if (diff < 0)
1596 direction = -1;
1597
1598 ++index;
1599 }
1600
1601 if (direction == 0)
1602 throw runtime_error("Sample log is flat. Use option 'Both' instead! ");
1603
1604 return direction;
1605}
1606
1607//----------------------------------------------------------------------------------------------
1610void GenerateEventsFilter::addNewTimeFilterSplitter(Types::Core::DateAndTime starttime,
1611 Types::Core::DateAndTime stoptime, int wsindex,
1612 const string &info) {
1613 if (m_forFastLog) {
1614 // For MatrixWorkspace splitter
1615 // Start of splitter
1616 if (m_vecSplitterTime.empty()) {
1617 // First splitter
1618 m_vecSplitterTime.emplace_back(starttime);
1619 } else if (m_vecSplitterTime.back() < starttime) {
1620 // Splitter to insert has a gap to previous splitter
1621 m_vecSplitterTime.emplace_back(starttime);
1622 m_vecSplitterGroup.emplace_back(-1);
1623
1624 } else if (m_vecSplitterTime.back() == starttime) {
1625 // Splitter to insert is just behind previous one (no gap): nothing
1626 } else {
1627 // Impossible situation
1628 throw runtime_error("Logic error. Trying to insert a splitter, whose "
1629 "start time is earlier than last splitter.");
1630 }
1631 // Stop of splitter
1632 m_vecSplitterTime.emplace_back(stoptime);
1633 // Group
1634 m_vecSplitterGroup.emplace_back(wsindex);
1635 } else {
1636 // For regular Splitter
1637 Kernel::SplittingInterval spiv(starttime, stoptime, wsindex);
1638 m_splitWS->addSplitter(spiv);
1639 }
1640
1641 // Information
1642 if (!info.empty()) {
1644 API::TableRow row = m_filterInfoWS->appendRow();
1645 row << wsindex << info;
1646 } else {
1647 m_filterInfoWS->insertRow(0);
1648 API::TableRow row = m_filterInfoWS->getRow(0);
1649 row << wsindex << info;
1650 }
1651 }
1652}
1653
1654//----------------------------------------------------------------------------------------------
1658DateAndTime GenerateEventsFilter::makeSplitterInVector(std::vector<Types::Core::DateAndTime> &vecSplitTime,
1659 std::vector<int> &vecGroupIndex, Types::Core::DateAndTime start,
1660 Types::Core::DateAndTime stop, int group, int64_t tol_ns,
1661 DateAndTime lasttime) {
1662 DateAndTime starttime(start.totalNanoseconds() - tol_ns);
1663 DateAndTime stoptime(stop.totalNanoseconds() - tol_ns);
1664 // DateAndTime starttime = start-tolerance;
1665 // DateAndTime stoptime = stop-tolerance;
1666
1667 size_t timevecsize = vecSplitTime.size();
1668 if (timevecsize > 0)
1669 lasttime = vecSplitTime.back();
1670
1671 // Start time of splitter
1672 if (timevecsize == 0) {
1673 // First value
1674 vecSplitTime.emplace_back(starttime);
1675 } else if (lasttime < starttime) {
1676 // Stop time of previous splitter is earlier than start time of this
1677 // splitter (gap)
1678 vecSplitTime.emplace_back(starttime);
1679 vecGroupIndex.emplace_back(-1);
1680 } else if (lasttime > starttime) {
1681 // Impossible situation
1682 throw runtime_error("Impossible situation.");
1683 }
1684
1685 // The last situation is "Stop time of previous splitter is the start time of
1686 // this splitter".
1687 // No action is required to take
1688
1689 // Complete this splitter, i.e., stoptime and group
1690 // Stop time of splitter
1691 vecSplitTime.emplace_back(stoptime);
1692 // Group index
1693 vecGroupIndex.emplace_back(group);
1694
1695 return stoptime;
1696}
1697
1698//----------------------------------------------------------------------------------------------
1702 g_log.information() << "Size of splitter vector: " << m_vecSplitterTime.size() << ", " << m_vecSplitterGroup.size()
1703 << "\n";
1704
1705 size_t sizex = m_vecSplitterTime.size();
1706 size_t sizey = m_vecSplitterGroup.size();
1707
1708 if (sizex - sizey != 1) {
1709 throw runtime_error("Logic error on splitter vectors' size. ");
1710 }
1711
1712 m_filterWS = create<Workspace2D>(1, BinEdges(sizex));
1713 auto &dataX = m_filterWS->mutableX(0);
1714 for (size_t i = 0; i < sizex; ++i) {
1715 // x is in the unit as second
1716 dataX[i] = static_cast<double>(m_vecSplitterTime[i].totalNanoseconds()) * 1.E-9;
1717 }
1718
1719 auto &dataY = m_filterWS->mutableY(0);
1720 for (size_t i = 0; i < sizey; ++i) {
1721 dataY[i] = static_cast<double>(m_vecSplitterGroup[i]);
1722 }
1723}
1724
1725//----------------------------------------------------------------------------------------------
1731 // Determine size of output matrix workspace
1732 size_t numtimes = 0;
1733 size_t numThreads = m_vecSplitterTimeSet.size();
1734 for (size_t i = 0; i < numThreads; ++i) {
1735 numtimes += m_vecGroupIndexSet[i].size();
1736 g_log.debug() << "[DB] Thread " << i << " have " << m_vecGroupIndexSet[i].size() << " splitter "
1737 << "\n";
1738 }
1739 ++numtimes;
1740
1741 size_t sizex = numtimes;
1742
1743 m_filterWS = create<Workspace2D>(1, BinEdges(sizex));
1744 auto &dataX = m_filterWS->mutableX(0);
1745 auto &dataY = m_filterWS->mutableY(0);
1746
1747 size_t index = 0;
1748 for (size_t i = 0; i < numThreads; ++i) {
1749 for (size_t j = 0; j < m_vecGroupIndexSet[i].size(); ++j) {
1750 // x is in the unit as second
1751 dataX[index] = static_cast<double>(m_vecSplitterTimeSet[i][j].totalNanoseconds()) * 1.E-9;
1752 dataY[index] = static_cast<double>(m_vecGroupIndexSet[i][j]);
1753 ++index;
1754 }
1755 }
1756 // x is in the unit as second
1757 dataX[index] = static_cast<double>(m_vecSplitterTimeSet.back().back().totalNanoseconds()) * 1.E-9;
1758}
1759
1760//----------------------------------------------------------------------------------------------
1764 for (size_t i = 0; i < m_vecSplitterGroup.size(); ++i) {
1765 int groupindex = m_vecSplitterGroup[i];
1766 if (groupindex >= 0) {
1767 DateAndTime start = m_vecSplitterTime[i];
1768 DateAndTime stop = m_vecSplitterTime[i + 1];
1769 Kernel::SplittingInterval newsplit(start, stop, groupindex);
1770 m_splitWS->addSplitter(newsplit);
1771 }
1772 }
1773}
1774
1775//----------------------------------------------------------------------------------------------
1784 // Try to get the run end from Run object
1785 DateAndTime runendtime(0);
1786 bool norunendset = false;
1787 try {
1788 runendtime = m_dataWS->run().endTime();
1789 } catch (const std::runtime_error &) {
1790 norunendset = true;
1791 }
1792
1793 if (norunendset) {
1794 DataObjects::EventWorkspace_const_sptr eventWS = std::dynamic_pointer_cast<const EventWorkspace>(m_dataWS);
1795 if (!eventWS || eventWS->getNumberEvents() == 0) {
1796 throw std::runtime_error("Run end time cannot be determined.");
1797 } else {
1798 runendtime = eventWS->getPulseTimeMax();
1799 }
1800 }
1801
1802 auto extended_ns = static_cast<int64_t>(1.0E8); // 0.1 seconds
1803 if (m_dataWS->run().hasProperty("proton_charge")) {
1804 Kernel::TimeSeriesProperty<double> const *protonchargelog =
1805 dynamic_cast<Kernel::TimeSeriesProperty<double> *>(m_dataWS->run().getProperty("proton_charge"));
1806 if (protonchargelog->size() > 1) {
1807 extended_ns = protonchargelog->nthTime(1).totalNanoseconds() - protonchargelog->nthTime(0).totalNanoseconds();
1808 }
1809 }
1810
1811 // Add last pulse time or 0.1 seconds to make sure that no event left behind
1812 runendtime = DateAndTime(runendtime.totalNanoseconds() + extended_ns);
1813
1814 return runendtime;
1815}
1816
1817} // 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 PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PRAGMA_OMP(expression)
#define PARALLEL_GET_MAX_THREADS
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
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.
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
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
size_t size() const
Returns the number of rows in the TableWorkspace.
Definition TableRow.h:45
A property class for workspaces.
GenerateEventsFilter : Generate an events-filter, i.e., a SplittersWorkspace according to user's requ...
bool m_forFastLog
Flag to output matrix workspace for fast log.
std::vector< Types::Core::DateAndTime > m_vecSplitterTime
Vector as date and time.
Types::Core::DateAndTime m_runEndTime
Run end time.
void exec() override
Implement abstract Algorithm methods.
int determineChangingDirection(int startindex)
Determine the chaning direction of log value.
std::vector< std::vector< Types::Core::DateAndTime > > m_vecSplitterTimeSet
void processSingleValueFilter(double minvalue, double maxvalue, bool filterincrease, bool filterdecrease)
Generate filters by single log value.
void makeMultipleFiltersByValues(std::map< size_t, int > indexwsindexmap, const std::vector< double > &logvalueranges, bool centre, bool filterIncrease, bool filterDecrease, Types::Core::DateAndTime startTime, Types::Core::DateAndTime stopTime)
Make multiple-log-value filters in serial.
void addNewTimeFilterSplitter(Types::Core::DateAndTime starttime, Types::Core::DateAndTime stoptime, int wsindex, const std::string &info)
Add a splitter.
const std::string name() const override
Algorithm's name for identification overriding a virtual method.
void init() override
Implement abstract Algorithm methods.
size_t searchValue(const std::vector< double > &sorteddata, double value)
Search a value in a sorted vector.
void setFilterByTimeOnly()
Set splitters by time value / interval only.
void generateSplittersInMatrixWorkspaceParallel()
Generate a matrix workspace from the parallel version.
bool identifyLogEntry(const int &index, const Types::Core::DateAndTime &currT, const bool &lastgood, const double &minvalue, const double &maxvalue, const Types::Core::DateAndTime &startT, const Types::Core::DateAndTime &stopT, const bool &filterIncrease, const bool &filterDecrease)
Identify the a sample log entry is within intended value and time region.
void setFilterByLogValue(const std::string &logname)
Generate filters by log values.
bool m_isReverseLogarithmic
Flag if is reverse logarithmic time binning.
API::MatrixWorkspace_const_sptr m_dataWS
API::MatrixWorkspace_sptr m_filterWS
Matrix workspace containing splitters.
void generateSplittersInMatrixWorkspace()
Generate a matrix workspace containing splitters.
Kernel::TimeSeriesProperty< int > * m_intLog
void processIntegerValueFilter(int minvalue, int maxvalue, bool filterIncrease, bool filterDecrease, Types::Core::DateAndTime runend)
Generate event filters for integer sample log.
void makeFilterBySingleValue(double min, double max, double TimeTolerance, bool centre, bool filterIncrease, bool filterDecrease, Types::Core::DateAndTime startTime, Types::Core::DateAndTime stopTime, int wsindex)
Fill a SplittingIntervalVec that will filter the events by matching SINGLE log values >= min and < ma...
void processInputTime()
Process the input for time.
void processMultipleValueFilters(double minvalue, double valueinterval, double maxvalue, bool filterincrease, bool filterdecrease)
Generate filters from multiple values.
void makeMultipleFiltersByValuesParallel(const std::map< size_t, int > &indexwsindexmap, const std::vector< double > &logvalueranges, bool centre, bool filterIncrease, bool filterDecrease, Types::Core::DateAndTime startTime, Types::Core::DateAndTime stopTime)
Make multiple-log-value filters in serial in parallel.
Kernel::TimeSeriesProperty< double > * m_dblLog
DataObjects::SplittersWorkspace_sptr m_splitWS
SplitterWorkspace.
Types::Core::DateAndTime findRunEnd()
Find the end of the run.
Types::Core::DateAndTime makeSplitterInVector(std::vector< Types::Core::DateAndTime > &vecSplitTime, std::vector< int > &vecGroupIndex, Types::Core::DateAndTime start, Types::Core::DateAndTime stop, int group, int64_t tol_ns, Types::Core::DateAndTime lasttime)
Create a splitter and add to the vector of time splitters.
std::vector< std::vector< int > > m_vecGroupIndexSet
bool m_useParallel
Processing algorithm type.
void makeMultipleFiltersByValuesPartialLog(int istart, int iend, std::vector< Types::Core::DateAndTime > &vecSplitTime, std::vector< int > &vecSplitGroup, std::map< size_t, int > indexwsindexmap, const std::vector< double > &logvalueranges, const Types::Core::time_duration &tol, bool filterIncrease, bool filterDecrease, Types::Core::DateAndTime startTime, Types::Core::DateAndTime stopTime)
Generate event splitters for partial sample log (serial)
void generateSplittersInSplitterWS()
Generate a SplittersWorkspace for filtering by log values.
SplittersWorkspace : A TableWorkspace to contain TimeSplitters.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
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 warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
bool is(int level) const
Returns true if at least the given log level is set.
Definition Logger.cpp:177
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
const std::string & name() const
Get the property's name.
Definition Property.cpp:61
Class holding a start/end time and a destination for splitting event lists and logs.
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.
TYPE minValue() const
Returns the minimum value found in the series.
TYPE maxValue() const
Returns the maximum value found in the series.
std::vector< TYPE > valuesAsVector() const
Return the time series's values (unfiltered) as a vector<TYPE>
void eliminateDuplicates()
Detects whether there are duplicated entries (of time) in property & eliminates them.
void addValue(const Types::Core::DateAndTime &time, const TYPE &value)
Add a value to the map using a DateAndTime object.
Types::Core::DateAndTime lastTime() const
Returns the last time.
virtual TYPE nthValue(int n) const
Returns n-th value of n-th interval in an incredibly inefficient way.
std::vector< Types::Core::DateAndTime > timesAsVector() const override
Return the time series's times as a vector<DateAndTime>
virtual Types::Core::DateAndTime nthTime(int n) const
Returns n-th time. NOTE: Complexity is order(n)! regardless of filter.
std::shared_ptr< const EventWorkspace > EventWorkspace_const_sptr
shared pointer to a const Workspace2D
Helper class which provides the Collimation Length for SANS instruments.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
STL namespace.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54