15#include "MantidHistogramData/Histogram.h"
20#include <boost/math/special_functions/round.hpp>
28using Types::Core::DateAndTime;
29using Types::Core::time_duration;
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() {}
50 "An input Matrix workspace.");
53 "The name to use for the output SplittersWorkspace object, ie the filter.");
57 "Optional output for the information of each splitter workspace index");
59 declareProperty(
"FastLog",
false,
"Fast log will make output workspace to be a matrix workspace. ");
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"
66 "Absolute time takes a string in format as 1990-01-01T00:00:00, while the relative time takes "
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"
73 "Absolute time takes a string in format as 1990-01-01T00:00:00, while the relative time takes "
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.");
86 std::vector<std::string> timeoptions{
"Seconds",
"Nanoseconds",
"Percent"};
87 declareProperty(
"UnitOfTime",
"Seconds", std::make_shared<Kernel::StringListValidator>(timeoptions),
88 "StartTime, StopTime and DeltaTime can be given in various unit; the unit can be 'Seconds' or "
89 "'Nanoseconds' from run start time or can also be defined as 'Percentage' of total run time.");
94 "Name of the sample log to use to filter - for example, the pulse charge is recorded in 'ProtonCharge'.");
104 "Delta of log value to be sliced into from min log value and max log value; if not given, then only value ");
107 std::vector<std::string> filteroptions{
"Both",
"Increase",
"Decrease"};
109 std::make_shared<Kernel::StringListValidator>(filteroptions),
110 "d(log value)/dt can be positive and negative, they can be put to different splitters\n"
111 "there are 3 options, 'Both', 'Increase' and 'Decrease' corresponding to d(log value)/dt being any "
112 "value, positive only and negative only respectively.");
114 std::make_unique<VisibleWhenProperty>(
"LogName",
IS_NOT_EQUAL_TO,
""));
117 "Tolerance, in seconds, for the event times to keep. It is used in the case to filter by single "
118 "value. How TimeTolerance is applied is highly correlated to LogBoundary and PulseFilter. Check the "
119 "help or algorithm documents for details.");
122 vector<string> logboundoptions{
"Centre",
"Left",
"Other"};
123 auto logvalidator = std::make_shared<StringListValidator>(logboundoptions);
125 "How to treat log values as being measured in the centre of time. "
126 "There are three options, 'Centre', 'Left' and 'Other'. "
127 "This value must be set to Left if the sample log is recorded upon changing,"
128 "which applies to most of the sample environment devices in SNS.");
133 "Tolerance of the log value to be included in filter, used in the case to filter by multiple values.");
137 declareProperty(
"TitleOfSplitters",
"",
"Title of output splitters workspace and information workspace.");
140 vector<string> processoptions{
"Serial",
"Parallel"};
141 auto procvalidator = std::make_shared<StringListValidator>(processoptions);
143 "Use multiple cores to generate events filter by log values: \n"
144 "Serial: Use a single core, good for slow log; \n"
145 "Parallel: Use multiple cores, appropriate for fast log. ");
148 declareProperty(
"UseReverseLogarithmic",
false,
"Use reverse logarithm for the time filtering.");
164 std::string logname = this->
getProperty(
"LogName");
165 if (logname.empty()) {
196 std::string title =
getProperty(
"TitleOfSplitters");
215 if (algtype ==
"Serial")
217 else if (algtype ==
"Parallel")
220 throw std::runtime_error(
"Impossible to have 3rd type other than Serial and Parallel. ");
224 g_log.
warning(
"Parallelization is for fast log only. Automatically turn "
237 std::string s_inpt0 = this->
getProperty(
"StartTime");
238 std::string s_inptf = this->
getProperty(
"StopTime");
241 bool defaultstart = s_inpt0.empty();
242 bool defaultstop = s_inptf.empty();
245 bool instringformat =
true;
246 if (!defaultstart && s_inpt0.find(
':') == std::string::npos) {
248 instringformat =
false;
249 }
else if (!defaultstop && s_inptf.find(
':') == std::string::npos) {
251 instringformat =
false;
255 DateAndTime runstarttime =
m_dataWS->run().startTime();
260 std::string timeunit = this->
getProperty(
"UnitOfTime");
262 if (timeunit ==
"Seconds") {
265 }
else if (timeunit ==
"Nanoseconds") {
268 }
else if (timeunit ==
"Percent") {
270 int64_t runtime_ns =
m_runEndTime.totalNanoseconds() - runstarttime.totalNanoseconds();
271 auto runtimed_ns =
static_cast<double>(runtime_ns);
276 errss <<
"TimeType " << timeunit <<
" is not supported.";
277 throw std::runtime_error(errss.str());
284 }
else if (instringformat) {
289 double inpt0 = std::stod(s_inpt0.c_str());
292 errss <<
"Input relative StartTime " << inpt0 <<
" cannot be negative. ";
293 throw std::invalid_argument(errss.str());
303 }
else if (instringformat) {
308 double inptf = std::stod(s_inptf.c_str());
317 errss <<
"Input StartTime " <<
m_startTime.toISO8601String() <<
" is equal or later than "
318 <<
"input StopTime " <<
m_stopTime.toFormattedString();
319 throw runtime_error(errss.str());
323 <<
"; Run start = " << runstarttime.toISO8601String()
324 <<
", Run stop = " <<
m_runEndTime.toISO8601String() <<
"\n";
331 vector<double> vec_timeintervals = this->
getProperty(
"TimeInterval");
333 bool singleslot =
false;
334 if (vec_timeintervals.empty()) {
338 if (std::all_of(vec_timeintervals.begin(), vec_timeintervals.end(), [](
double i) { return i == 0; }))
339 throw std::invalid_argument(
"If TimeInterval has one or more values, at "
340 "least one of those values must be non-zero.");
347 <<
"stop @ " <<
m_stopTime.totalNanoseconds() <<
"\n";
353 std::stringstream ss;
356 }
else if (vec_timeintervals.size() == 1) {
357 double timeinterval = vec_timeintervals[0];
358 int64_t timeslot = 0;
360 int64_t runStartTime =
m_dataWS->run().startTime().totalNanoseconds();
361 bool isLogarithmic = (timeinterval < 0);
365 g_log.
warning(
"UseReverseLogarithmic checked but linear time interval provided. Using linear time interval.");
369 if (isLogarithmic &&
m_startTime.totalNanoseconds() == runStartTime)
370 throw runtime_error(
"Cannot do logarithmic time interval if the start time is the same as the start of the run.");
373 double factor = std::fabs(timeinterval);
375 int64_t startTime_ns =
m_startTime.totalNanoseconds();
376 int64_t endTime_ns =
m_stopTime.totalNanoseconds();
378 double relativeStartTime_ns =
static_cast<double>(startTime_ns - runStartTime);
379 double relativeEndTime_ns =
static_cast<double>(endTime_ns - runStartTime);
383 int64_t initialReverseLogStep = startTime_ns - runStartTime;
385 int totalNumberOfSlices;
391 double logSize = std::log(relativeEndTime_ns / relativeStartTime_ns) / std::log1p(factor);
393 totalNumberOfSlices =
static_cast<int>(std::ceil(logSize));
396 totalNumberOfSlices = 1;
398 double previousBin = std::pow(1 + factor, std::floor(logSize) - 1);
401 if (relativeEndTime_ns - relativeStartTime_ns * previousBin * (1 + factor) >
402 relativeStartTime_ns * previousBin * factor) {
404 totalNumberOfSlices =
static_cast<int>(std::ceil(logSize));
407 totalNumberOfSlices =
static_cast<int>(std::floor(logSize));
413 totalNumberOfSlices =
414 static_cast<int>((relativeEndTime_ns - relativeStartTime_ns) /
static_cast<double>(deltatime_ns));
426 int64_t step = initialReverseLogStep + endTime_ns - runStartTime - curtime_ns;
427 nexttime_ns = curtime_ns -
static_cast<int64_t
>(
static_cast<double>(step) * factor);
429 nexttime_ns =
static_cast<int64_t
>(
static_cast<double>(curtime_ns) * (1 + factor));
432 nexttime_ns = curtime_ns + deltatime_ns;
434 if (nexttime_ns + runStartTime >
m_stopTime.totalNanoseconds())
435 nexttime_ns =
m_stopTime.totalNanoseconds() - runStartTime;
438 if (runStartTime + nexttime_ns - (curtime_ns - nexttime_ns) <
m_startTime.totalNanoseconds())
439 nexttime_ns =
m_startTime.totalNanoseconds() - runStartTime;
442 Types::Core::DateAndTime t0(std::min(curtime_ns, nexttime_ns) + runStartTime);
443 Types::Core::DateAndTime tf(std::max(curtime_ns, nexttime_ns) + runStartTime);
444 std::stringstream ss;
445 ss <<
"Time.Interval.From." << t0 <<
".to." << tf;
450 curtime_ns = nexttime_ns;
455 newtimeslot = (endTime_ns - (curtime_ns + runStartTime)) * 90 / totaltime;
458 newtimeslot = (curtime_ns + runStartTime - startTime_ns) * 90 / totaltime;
461 if (newtimeslot > timeslot) {
463 timeslot = newtimeslot;
464 double prog = 0.1 + double(timeslot) / 100.0;
474 size_t numtimeintervals = vec_timeintervals.size();
475 std::vector<int64_t> vec_dtimens(numtimeintervals);
476 for (
size_t id = 0;
id < numtimeintervals; ++id) {
478 vec_dtimens[id] = deltatime_ns;
482 int64_t timeslot = 0;
484 int64_t curtime_ns =
m_startTime.totalNanoseconds();
486 while (curtime_ns <
m_stopTime.totalNanoseconds()) {
487 for (
size_t id = 0;
id < numtimeintervals; ++id) {
489 int64_t deltatime_ns = vec_dtimens[id];
491 int64_t nexttime_ns = curtime_ns + deltatime_ns;
492 bool breaklater =
false;
493 if (nexttime_ns >
m_stopTime.totalNanoseconds()) {
499 Types::Core::DateAndTime t0(curtime_ns);
500 Types::Core::DateAndTime tf(nexttime_ns);
501 std::stringstream ss;
502 ss <<
"Time.Interval.From." << t0 <<
".to." << tf;
507 curtime_ns = nexttime_ns;
511 int64_t newtimeslot = (curtime_ns -
m_startTime.totalNanoseconds()) * 90 / totaltime;
512 if (newtimeslot > timeslot) {
514 timeslot = newtimeslot;
515 double prog = 0.1 + double(timeslot) / 100.0;
536 errmsg <<
"Log " << logname <<
" does not exist or is not TimeSeriesProperty in double or integer.";
537 throw runtime_error(errmsg.str());
542 g_log.
debug(
"Attempting to remove duplicates in double series log.");
547 g_log.
debug(
"Attempting to remove duplicates in integer series log.");
553 double minvalue = this->
getProperty(
"MinimumLogValue");
554 double maxvalue = this->
getProperty(
"MaximumLogValue");
555 double deltaValue = this->
getProperty(
"LogValueInterval");
558 std::string filterdirection =
getProperty(
"FilterLogValueByChangingDirection");
561 if (filterdirection ==
"Both") {
562 filterIncrease =
true;
563 filterDecrease =
true;
564 }
else if (filterdirection ==
"Increase") {
566 filterIncrease =
true;
567 filterDecrease =
false;
569 filterIncrease =
false;
570 filterDecrease =
true;
573 bool toProcessSingleValueFilter =
false;
575 toProcessSingleValueFilter =
true;
576 }
else if (deltaValue < 0) {
577 throw runtime_error(
"Delta value cannot be negative.");
597 if (minvalue > maxvalue) {
599 errmsg <<
"Fatal Error: Input minimum log value " << minvalue <<
" is larger than maximum log value " << maxvalue;
600 throw runtime_error(errmsg.str());
602 g_log.
debug() <<
"Filter by log value: min = " << minvalue <<
", max = " << maxvalue
603 <<
", process single value? = " << toProcessSingleValueFilter <<
", delta value = " << deltaValue
608 if (toProcessSingleValueFilter) {
618 int minvaluei, maxvaluei;
621 minvalue =
static_cast<double>(minvaluei);
623 minvaluei = boost::math::iround(minvalue);
627 maxvalue =
static_cast<double>(maxvaluei);
629 maxvaluei = boost::math::iround(maxvalue);
631 if (minvalue > maxvalue) {
633 errmsg <<
"Fatal Error: Input minimum log value " << minvalue <<
" is larger than maximum log value " << maxvalue;
634 throw runtime_error(errmsg.str());
636 g_log.
information() <<
"Generate event-filter for integer log: min = " << minvaluei <<
", "
637 <<
"max = " << maxvaluei <<
"\n";
641 DateAndTime runendtime =
m_dataWS->run().endTime();
647 <<
"maximum value = " << maxvalue <<
".\n";
660 bool filterdecrease) {
662 double timetolerance = this->
getProperty(
"TimeTolerance");
665 std::string logboundary = this->
getProperty(
"LogBoundary");
666 transform(logboundary.begin(), logboundary.end(), logboundary.begin(), ::tolower);
671 makeFilterBySingleValue(minvalue, maxvalue,
static_cast<double>(timetolerance_ns) * 1.0E-9, logboundary ==
"centre",
676 throw runtime_error(
"m_filterInfoWS has not been initialized.");
679 std::stringstream ss;
680 ss <<
"Log." <<
m_dblLog->
name() <<
".From." << minvalue <<
".To." << maxvalue <<
".Value-change-direction:";
681 if (filterincrease && filterdecrease) {
683 }
else if (filterincrease) {
688 row << 0 << ss.str();
702 bool filterincrease,
bool filterdecrease) {
704 if (valueinterval <= 0)
705 throw std::invalid_argument(
"Multiple values filter must have LogValueInterval larger than ZERO.");
706 double valuetolerance = this->
getProperty(
"LogValueTolerance");
709 valuetolerance = 0.5 * valueinterval;
710 else if (valuetolerance < 0.0)
711 throw std::runtime_error(
"LogValueTolerance cannot be less than zero.");
715 std::map<size_t, int> indexwsindexmap;
716 std::vector<double> logvalueranges;
720 double curvalue = minvalue;
721 while (curvalue - valuetolerance < maxvalue) {
722 indexwsindexmap.emplace(
index, wsindex);
725 double lowbound = curvalue - valuetolerance;
726 double upbound = curvalue + valueinterval - valuetolerance;
727 logvalueranges.emplace_back(lowbound);
728 logvalueranges.emplace_back(upbound);
731 std::stringstream ss;
732 ss <<
"Log." <<
m_dblLog->
name() <<
".From." << lowbound <<
".To." << upbound <<
".Value-change-direction:";
733 if (filterincrease && filterdecrease) {
735 }
else if (filterincrease) {
741 newrow << wsindex << ss.str();
743 curvalue += valueinterval;
749 stringstream dbsplitss;
750 dbsplitss <<
"Index map size = " << indexwsindexmap.size() <<
"\n";
751 for (
auto &mit : indexwsindexmap) {
752 dbsplitss <<
"Index " << mit.first <<
": WS-group = " << mit.second <<
". Log value range: ["
753 << logvalueranges[mit.first * 2] <<
", " << logvalueranges[mit.first * 2 + 1] <<
").\n";
758 if (logvalueranges.size() < 2) {
759 g_log.
warning(
"There is no log value interval existing.");
765 double upperboundinterval0 = logvalueranges[1];
766 double lowerboundlastinterval = logvalueranges[logvalueranges.size() - 2];
769 if (minlogvalue > upperboundinterval0 || maxlogvalue < lowerboundlastinterval) {
770 g_log.
warning() <<
"User specifies log interval from " << minvalue - valuetolerance <<
" to "
771 << maxvalue - valuetolerance <<
" with interval size = " << valueinterval <<
"; Log "
772 <<
m_dblLog->
name() <<
" has range " << minlogvalue <<
" to " << maxlogvalue
773 <<
". Therefore some workgroup index may not have any splitter.\n";
778 std::string logboundary = this->
getProperty(
"LogBoundary");
779 transform(logboundary.begin(), logboundary.end(), logboundary.begin(), ::tolower);
813 bool filterIncrease,
bool filterDecrease, DateAndTime startTime,
814 Types::Core::DateAndTime stopTime,
int wsindex) {
817 g_log.
warning() <<
"There is no entry in this property " << this->
name() <<
"\n";
826 bool lastGood =
false;
827 time_duration tol = DateAndTime::durationFromSeconds(TimeTolerance);
829 DateAndTime currT, start, stop;
837 bool isGood =
identifyLogEntry(i, currT, lastGood, min, max, startTime, stopTime, filterIncrease, filterDecrease);
842 if (isGood != lastGood) {
858 std::string empty(
"");
869 if (tmpslot > progslot) {
871 double prog = double(progslot) / 100.0 + 0.1;
885 std::string empty(
"");
902 const bool &lastgood,
const double &minvalue,
const double &maxvalue,
903 const Types::Core::DateAndTime &startT,
904 const Types::Core::DateAndTime &stopT,
const bool &filterIncrease,
905 const bool &filterDecrease) {
909 bool isgood = (val >= minvalue && val < maxvalue) && (currT >= startT && currT < stopT);
912 if (isgood && (!filterIncrease || !filterDecrease)) {
915 if (
index < numlogentries - 1) {
923 if (diff > 0 && filterIncrease)
925 else if (diff < 0 && filterDecrease)
953 const vector<double> &logvalueranges,
bool centre,
954 bool filterIncrease,
bool filterDecrease, DateAndTime startTime,
955 DateAndTime stopTime) {
956 g_log.
notice(
"Starting method 'makeMultipleFiltersByValues'. ");
966 double timetolerance = 0.0;
968 timetolerance = this->
getProperty(
"TimeTolerance");
970 time_duration tol = DateAndTime::durationFromSeconds(timetolerance);
974 vector<DateAndTime> tempvectimes;
976 vector<int> tempvecgroup;
981 auto iend =
static_cast<int>(logsize - 1);
984 logvalueranges, tol, filterIncrease, filterDecrease, startTime, stopTime);
1007 const vector<double> &logvalueranges,
bool centre,
1008 bool filterIncrease,
bool filterDecrease,
1009 DateAndTime startTime, DateAndTime stopTime) {
1018 double timetolerance = 0.0;
1020 timetolerance = this->
getProperty(
"TimeTolerance");
1022 time_duration tol = DateAndTime::durationFromSeconds(timetolerance);
1030 numThreads = std::min(numThreads, logsize / 8);
1035 std::vector<int> vecStart, vecEnd;
1036 int partloglength = (logsize - 1) / numThreads;
1037 int extra = (logsize - 1) % numThreads;
1038 for (
int i = 0; i < numThreads; ++i) {
1039 int istart = i * partloglength;
1045 int iend = istart + partloglength;
1049 vecStart.emplace_back(istart);
1050 vecEnd.emplace_back(iend);
1055 dbss <<
"Number of thread = " << numThreads <<
", Log Size = " << logsize <<
"\n";
1056 for (
size_t i = 0; i < vecStart.size(); ++i) {
1057 dbss <<
"Thread " << i <<
": Log range: [" << vecStart[i] <<
", " << vecEnd[i] <<
") "
1058 <<
"Size = " << vecEnd[i] - vecStart[i] <<
"\n";
1066 for (
int i = 0; i < numThreads; ++i) {
1067 vector<DateAndTime> tempvectimes;
1069 vector<int> tempvecgroup;
1077 PRAGMA_OMP(parallel
for schedule(dynamic, 1) )
1078 for (
int i = 0; i < numThreads; ++i) {
1081 int istart = vecStart[i];
1082 int iend = vecEnd[i];
1085 indexwsindexmap, logvalueranges, tol, filterIncrease, filterDecrease,
1086 startTime, stopTime);
1092 for (
int i = 1; i < numThreads; ++i) {
1101 errss <<
"Previous vector of group index set (" << (i - 1) <<
") is equal to -1. "
1102 <<
"It is not likely to happen! Size of previous vector of "
1105 throw runtime_error(errss.str());
1114 g_log.
debug() <<
"Splitter at the end of thread " << i <<
" is extended from " << origtime <<
" to " << newt0
1126 if (lastindex != -1 && firstindex != -1) {
1134 g_log.
debug() <<
"Thread = " << i <<
", change ending time of " << i - 1 <<
" to "
1141 throw runtime_error(
"It is not possible to have start or end of "
1142 "filter to be minus-one index. ");
1154 int istart,
int iend, std::vector<Types::Core::DateAndTime> &vecSplitTime, std::vector<int> &vecSplitGroup,
1155 map<size_t, int> indexwsindexmap,
const vector<double> &logvalueranges,
const time_duration &tol,
1156 bool filterIncrease,
bool filterDecrease, DateAndTime startTime, DateAndTime stopTime) {
1159 if (istart < 0 || iend >= logsize)
1160 throw runtime_error(
"Input index of makeMultipleFiltersByValuesPartialLog "
1161 "is out of boundary. ");
1163 int64_t tol_ns = tol.total_nanoseconds();
1166 const Types::Core::DateAndTime ZeroTime(0);
1169 DateAndTime currTime, start, stop;
1172 g_log.
information() <<
"Log time coverage (index: " << istart <<
", " << iend <<
") from "
1175 DateAndTime laststoptime(0);
1180 for (
int i = istart; i <= iend; i++) {
1182 bool breakloop =
false;
1183 bool createsplitter =
false;
1189 if (currTime < startTime) {
1191 createsplitter =
false;
1192 }
else if (currTime > stopTime) {
1197 if (start.totalNanoseconds() > 0) {
1198 createsplitter =
true;
1203 bool newsplitter =
false;
1207 if (i < lastlogindex) {
1215 direction = prevDirection;
1218 direction = prevDirection;
1220 prevDirection = direction;
1224 bool correctdir =
true;
1225 if (filterIncrease && filterDecrease) {
1230 if (filterIncrease && direction > 0)
1232 else if (filterDecrease && direction < 0)
1234 else if (direction == 0)
1235 throw runtime_error(
"Direction is not determined.");
1245 bool valueWithinMinMax =
true;
1246 if (
index > logvalueranges.size()) {
1248 valueWithinMinMax =
false;
1251 if (
g_log.
is(Logger::Priority::PRIO_DEBUG)) {
1253 dbss <<
"[DBx257] Examine Log Index " << i <<
", Value = " << currValue <<
", Data Range Index = " <<
index
1255 <<
"Group Index = " << indexwsindexmap[
index / 2]
1256 <<
" (log value range vector size = " << logvalueranges.size() <<
"): ";
1258 dbss << logvalueranges[
index] <<
", " << logvalueranges[
index + 1];
1259 else if (
index == logvalueranges.size())
1260 dbss << logvalueranges[
index - 1] <<
", " << logvalueranges[
index];
1261 else if (valueWithinMinMax)
1262 dbss << logvalueranges[
index - 1] <<
", " << logvalueranges[
index] <<
", " << logvalueranges[
index + 1];
1266 if (valueWithinMinMax) {
1267 if (
index % 2 == 0) {
1269 currindex = indexwsindexmap[
index / 2];
1271 if (currindex != lastindex && start.totalNanoseconds() == 0) {
1275 }
else if (currindex != lastindex && start.totalNanoseconds() > 0) {
1279 createsplitter =
true;
1281 }
else if (currindex == lastindex && start.totalNanoseconds() > 0) {
1287 createsplitter =
true;
1288 newsplitter =
false;
1295 std::stringstream errmsg;
1297 errmsg <<
"Impossible to have currindex == lastindex == " << currindex
1298 <<
", while start is not init. Log Index = " << i <<
"\t value = " << currValue
1299 <<
"\t, Index = " <<
index <<
" in range " << logvalueranges[
index] <<
", "
1300 << logvalueranges[
index + 1] <<
"; Last value = " << lastvalue;
1301 throw std::runtime_error(errmsg.str());
1307 g_log.
warning() <<
"Not likely to happen! Current value = " << currValue
1308 <<
" is within value range but its index = " <<
index <<
" has no map to group index. "
1310 if (start.totalNanoseconds() > 0) {
1313 createsplitter =
true;
1319 if (start.totalNanoseconds() > 0) {
1322 createsplitter =
true;
1332 if (start.totalNanoseconds() > 0) {
1334 createsplitter =
true;
1339 if (createsplitter) {
1341 makeSplitterInVector(vecSplitTime, vecSplitGroup, start, stop, lastindex, tol_ns, laststoptime);
1357 lastindex = currindex;
1364 if (vecSplitTime.empty()) {
1368 makeSplitterInVector(vecSplitTime, vecSplitGroup, start, stop, lastindex, tol_ns, laststoptime);
1381 bool filterDecrease, DateAndTime runend) {
1384 bool singlevaluemode;
1385 if (minvalue == maxvalue) {
1386 singlevaluemode =
true;
1388 singlevaluemode =
false;
1390 double deltadbl =
getProperty(
"LogValueInterval");
1392 delta = maxvalue - minvalue + 1;
1394 delta = boost::math::iround(deltadbl);
1398 errss <<
"In a non-single value mode, LogValueInterval cannot be 0 or "
1399 "negative for integer. "
1400 <<
"Current input is " << deltadbl <<
".";
1401 throw runtime_error(errss.str());
1412 int64_t timetolns = timetol.total_nanoseconds();
1414 DateAndTime splitstarttime(0);
1416 DateAndTime laststoptime(0);
1418 g_log.
debug() <<
"Number of integer log entries = " << numlogentries <<
".\n";
1420 for (
size_t i = 0; i < numlogentries; ++i) {
1421 int currvalue = vecValue[i];
1426 if (currvalue >= minvalue && currvalue <= maxvalue) {
1428 if ((i == 0) || (filterIncrease && vecValue[i] >= vecValue[i - 1]) ||
1429 (filterDecrease && vecValue[i] <= vecValue[i - 1])) {
1432 if (singlevaluemode) {
1435 currgroup = (currvalue - minvalue) /
delta;
1442 if (pregroup >= 0 && currgroup < 0) {
1445 if (splitstarttime.totalNanoseconds() == 0)
1446 throw runtime_error(
"Programming logic error.");
1450 laststoptime = vecTimes[i];
1452 splitstarttime = DateAndTime(0);
1453 statuschanged =
true;
1454 }
else if (pregroup < 0 && currgroup >= 0) {
1457 splitstarttime = vecTimes[i];
1458 statuschanged =
true;
1459 }
else if (currgroup >= 0 && pregroup != currgroup) {
1461 if (splitstarttime.totalNanoseconds() == 0)
1462 throw runtime_error(
"Programming logic error (1).");
1465 laststoptime = vecTimes[i];
1467 splitstarttime = vecTimes[i];
1468 statuschanged =
true;
1471 statuschanged =
false;
1476 pregroup = currgroup;
1480 if (pregroup >= 0) {
1482 if (splitstarttime.totalNanoseconds() == 0)
1483 throw runtime_error(
"Programming logic error (1).");
1490 if (singlevaluemode) {
1492 stringstream message;
1494 newrow << 0 << message.str();
1496 int logvalue = minvalue;
1498 while (logvalue <= maxvalue) {
1499 stringstream message;
1500 if (logvalue +
delta - 1 > logvalue)
1501 message <<
m_intLog->
name() <<
"=[" << logvalue <<
"," << logvalue +
delta - 1 <<
"]";
1505 message <<
".Value change direction:";
1506 if (filterIncrease && filterDecrease)
1508 else if (filterIncrease)
1509 message <<
"Increasing";
1510 else if (filterDecrease)
1511 message <<
"Decreasing";
1514 newrow << wsindex << message.str();
1522 <<
", Number of split info = " <<
m_filterInfoWS->rowCount() <<
".\n";
1537 size_t numdata = sorteddata.size();
1538 size_t outrange = numdata + 1;
1541 else if (
value < sorteddata.front() ||
value > sorteddata.back())
1546 static_cast<size_t>(std::lower_bound(sorteddata.begin(), sorteddata.end(),
value) - sorteddata.begin());
1557 if (
index == sorteddata.size())
1571 int index = startindex;
1572 while (direction == 0 &&
index > 0) {
1588 while (direction == 0 &&
index < maxindex) {
1599 throw runtime_error(
"Sample log is flat. Use option 'Both' instead! ");
1608 Types::Core::DateAndTime stoptime,
int wsindex,
1609 const string &info) {
1625 throw runtime_error(
"Logic error. Trying to insert a splitter, whose "
1626 "start time is earlier than last splitter.");
1639 if (!info.empty()) {
1642 row << wsindex << info;
1646 row << wsindex << info;
1656 std::vector<int> &vecGroupIndex, Types::Core::DateAndTime start,
1657 Types::Core::DateAndTime stop,
int group, int64_t tol_ns,
1658 DateAndTime lasttime) {
1659 DateAndTime starttime(start.totalNanoseconds() - tol_ns);
1660 DateAndTime stoptime(stop.totalNanoseconds() - tol_ns);
1664 size_t timevecsize = vecSplitTime.size();
1665 if (timevecsize > 0)
1666 lasttime = vecSplitTime.back();
1669 if (timevecsize == 0) {
1671 vecSplitTime.emplace_back(starttime);
1672 }
else if (lasttime < starttime) {
1675 vecSplitTime.emplace_back(starttime);
1676 vecGroupIndex.emplace_back(-1);
1677 }
else if (lasttime > starttime) {
1679 throw runtime_error(
"Impossible situation.");
1688 vecSplitTime.emplace_back(stoptime);
1690 vecGroupIndex.emplace_back(group);
1705 if (sizex - sizey != 1) {
1706 throw runtime_error(
"Logic error on splitter vectors' size. ");
1709 m_filterWS = create<Workspace2D>(1, BinEdges(sizex));
1711 for (
size_t i = 0; i < sizex; ++i) {
1713 dataX[i] =
static_cast<double>(
m_vecSplitterTime[i].totalNanoseconds()) * 1.E-9;
1717 for (
size_t i = 0; i < sizey; ++i) {
1729 size_t numtimes = 0;
1731 for (
size_t i = 0; i < numThreads; ++i) {
1738 size_t sizex = numtimes;
1740 m_filterWS = create<Workspace2D>(1, BinEdges(sizex));
1745 for (
size_t i = 0; i < numThreads; ++i) {
1763 if (groupindex >= 0) {
1786 DateAndTime runendtime(0);
1787 bool norunendset =
false;
1789 runendtime =
m_dataWS->run().endTime();
1790 }
catch (
const std::runtime_error &) {
1795 <<
"Run end time = " << runendtime <<
"/" << runendtime.totalNanoseconds()
1796 <<
", no run end set = " << norunendset <<
"\n";
1798 auto extended_ns =
static_cast<int64_t
>(1.0E8);
1799 if (
m_dataWS->run().hasProperty(
"proton_charge")) {
1805 if (!protonchargelog) {
1806 throw std::runtime_error(
"proton_charge log not found");
1809 if (protonchargelog->
size() > 1) {
1810 Types::Core::DateAndTime tmpendtime = protonchargelog->
lastTime();
1811 extended_ns = protonchargelog->
nthTime(1).totalNanoseconds() - protonchargelog->
nthTime(0).totalNanoseconds();
1812 if (tmpendtime > runendtime) {
1814 runendtime = tmpendtime;
1816 <<
"Use last proton charge time = " << tmpendtime.totalNanoseconds() <<
" as run end. "
1819 norunendset =
false;
1823 <<
" run end time = " << runendtime <<
"\n";
1824 }
else if (norunendset) {
1826 norunendset =
false;
1833 errss <<
"Input workspace " <<
m_dataWS->getName()
1834 <<
" is not an Eventworkspace and does not have sample log "
1836 "Therefore it fails to find run end time.";
1839 throw std::runtime_error(errss.str());
1840 }
else if (eventWS->getNumberEvents() == 0) {
1842 errss <<
"Input EventWorkspace " <<
m_dataWS->getName()
1843 <<
" has zero event and does not have sample log 'proton_charge'. "
1844 "Therefore, unable to "
1845 "determine run end time";
1848 throw std::runtime_error(errss.str());
1851 for (
size_t i = 0; i < eventWS->getNumberHistograms(); ++i) {
1856 if (lastpulse > runendtime)
1857 runendtime = lastpulse;
1861 <<
" from last event: run end time = " << runendtime <<
" / " << runendtime.totalNanoseconds()
1867 throw runtime_error(
"Run end time cannot be determined. ");
1871 runendtime = DateAndTime(runendtime.totalNanoseconds() + extended_ns);
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
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.
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.
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.
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.
double m_timeUnitConvertFactorToNS
std::vector< Types::Core::DateAndTime > m_vecSplitterTime
Vector as date and time.
void processInOutWorkspaces()
Process properties.
Types::Core::DateAndTime m_startTime
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.
API::ITableWorkspace_sptr m_filterInfoWS
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 TimeSplitterType that will filter the events by matching SINGLE log values >= min and < max.
void processInputTime()
Process the input for time.
Types::Core::DateAndTime m_stopTime
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.
double m_logTimeTolerance
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)
std::vector< int > m_vecSplitterGroup
void generateSplittersInSplitterWS()
Generate a SplittersWorkspace for filtering by log values.
std::size_t getNumberEvents() const override
Return the number of events in the list.
Mantid::Types::Core::DateAndTime getPulseTimeMax() const override
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.
void notice(const std::string &msg)
Logs at notice level.
void error(const std::string &msg)
Logs at error level.
void warning(const std::string &msg)
Logs at warning level.
bool is(int level) const
Returns true if at least the given log level is set.
void information(const std::string &msg)
Logs at information level.
const std::string & name() const
Get the property's name.
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.
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>
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.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
@ Input
An input workspace.
@ Output
An output workspace.