13#include "MantidNexus/NexusFile.h"
15#include <json/value.h>
17#include <boost/regex.hpp>
21using namespace Types::Core;
25Logger
g_log(
"TimeSeriesProperty");
33template <
typename TYPE>
bool allValuesAreSame(
const std::vector<TimeValueUnit<TYPE>> &values) {
34 const std::size_t num_values = values.size();
35 assert(num_values > 1);
36 const auto &first_value = values.front().value();
37 for (std::size_t i = 1; i < num_values; ++i) {
38 if (first_value != values[i].
value())
49template <
typename TYPE>
59template <
typename TYPE>
61 const std::vector<Types::Core::DateAndTime> ×,
62 const std::vector<TYPE> &values)
81template <
typename TYPE>
95 createFilteredData(timeROI, filteredTS->m_values);
97 filteredTS->m_size =
static_cast<int>(filteredTS->m_values.size());
107 auto timeSeriesProperty = this->clone();
108 auto values = timeSeriesProperty->valuesAsVector();
109 auto times = timeSeriesProperty->timesAsVector();
111 for (
auto it = times.begin(); it != times.end(); ++it) {
117 timeSeriesProperty->clear();
118 timeSeriesProperty->addValues(times, values);
119 return timeSeriesProperty;
131 if (this->m_values.size() < 2) {
132 throw std::runtime_error(
"Derivative is not defined for a time-series "
133 "property with less then two values");
136 this->sortIfNecessary();
137 auto it = this->m_values.begin();
138 int64_t t0 = it->time().totalNanoseconds();
139 TYPE v0 = it->value();
142 auto timeSeriesDeriv = std::make_unique<TimeSeriesProperty<double>>(this->
name() +
"_derivative");
143 timeSeriesDeriv->reserve(this->m_values.size() - 1);
144 for (; it != m_values.end(); it++) {
145 TYPE v1 = it->value();
146 int64_t t1 = it->time().totalNanoseconds();
148 double deriv = 1.e+9 * (double(v1 - v0) / double(t1 - t0));
149 auto tm =
static_cast<int64_t
>((t1 + t0) / 2);
150 timeSeriesDeriv->addValue(Types::Core::DateAndTime(tm), deriv);
155 return timeSeriesDeriv;
159 throw std::runtime_error(
"Time series property derivative is not defined for strings");
167 return m_values.size() * (
sizeof(
TYPE) +
sizeof(DateAndTime));
188 if (this->
operator!=(*
rhs)) {
189 m_values.insert(m_values.end(),
rhs->m_values.begin(),
rhs->m_values.end());
198 m_size =
static_cast<int>(m_values.size());
202 <<
" could not be added to another property of the same "
203 "name but incompatible type.\n";
225 if (this->realSize() !=
right.realSize()) {
228 const std::vector<DateAndTime> lhsTimes = this->timesAsVector();
229 const std::vector<DateAndTime> rhsTimes =
right.timesAsVector();
230 if (!std::equal(lhsTimes.begin(), lhsTimes.end(), rhsTimes.begin())) {
234 const std::vector<TYPE> lhsValues = this->valuesAsVector();
235 const std::vector<TYPE> rhsValues =
right.valuesAsVector();
236 if (!std::equal(lhsValues.begin(), lhsValues.end(), rhsValues.begin())) {
262 return !(*
this ==
right);
271 return !(*
this ==
right);
287template <
typename TYPE>
290 filteredData.clear();
295 if (m_values.empty()) {
300 if (m_values.size() == 1) {
301 filteredData.push_back(m_values.front());
308 if (allValuesAreSame(m_values) && this->
name() !=
"proton_charge") {
309 filteredData.push_back(m_values.front());
315 std::copy(m_values.cbegin(), m_values.cend(), std::back_inserter(filteredData));
321 filteredData.push_back(m_values.front());
328 const std::vector<Types::Core::DateAndTime> &roiTimes = timeROI.
getAllTimes();
329 auto itROI = roiTimes.cbegin();
330 const auto itROIEnd = roiTimes.cend();
332 auto itValue = m_values.cbegin();
333 const auto itValueEnd = m_values.cend();
334 auto itLastValueUsed = itValue;
336 while (itROI != itROIEnd && itValue != itValueEnd) {
339 while (std::distance(itROI, itROIEnd) > 2 && *(std::next(itROI, 2)) <= itValue->time())
340 std::advance(itROI, 2);
342 itValue = std::lower_bound(itValue, itValueEnd, *itROI,
343 [](
const auto &
value,
const auto &roi_time) {
return value.time() < roi_time; });
345 auto itBeginUseValue = itValue;
346 auto itEndUseValue = itValue;
348 if (itValue == itValueEnd) {
351 itEndUseValue = itValueEnd;
354 else if (itValue->time() <= *(std::next(itROI))) {
356 itBeginUseValue = itValue == m_values.begin() ? itValue : std::prev(itValue);
358 while (itValue != itValueEnd && itValue->time() <= *(std::next(itROI)))
361 itEndUseValue = itValue == itValueEnd ? itValue : std::next(itValue);
365 else if (std::distance(itROI, itROIEnd) == 2 ||
366 (std::distance(itROI, itROIEnd) > 2 && itValue->time() < *(std::next(itROI, 2)))) {
368 itBeginUseValue = itValue == m_values.begin() ? itValue : std::prev(itValue);
369 itEndUseValue = std::next(itValue);
372 if (!filteredData.empty()) {
373 itBeginUseValue = std::max(itBeginUseValue, std::next(itLastValueUsed));
377 if (itBeginUseValue < itEndUseValue) {
378 std::copy(itBeginUseValue, itEndUseValue, std::back_inserter(filteredData));
379 itLastValueUsed = std::prev(itEndUseValue);
383 std::advance(itROI, 2);
393 std::vector<TimeValueUnit<TYPE>> mp_copy;
394 createFilteredData(timeROI, mp_copy);
400 m_size =
static_cast<int>(m_values.size());
411#pragma warning(disable : 4244)
412#pragma warning(disable : 4804)
415#if defined(__GNUC__) && !(defined(__INTEL_COMPILER))
416#pragma GCC diagnostic ignored "-Wconversion"
432template <
typename TYPE>
434 double TimeTolerance,
bool centre)
const {
435 const bool emptyMin = (min ==
EMPTY_DBL());
436 const bool emptyMax = (max ==
EMPTY_DBL());
438 if (!emptyMin && !emptyMax && max < min) {
439 std::stringstream ss;
440 ss <<
"TimeSeriesProperty::makeFilterByValue: 'max' argument must be "
441 "greater than 'min' "
442 <<
"(got min=" << min <<
" max=" << max <<
")";
443 throw std::invalid_argument(ss.str());
449 min =
static_cast<double>(minValue());
451 max =
static_cast<double>(maxValue());
457 if (m_values.empty())
464 bool lastGood(
false);
465 time_duration tol = DateAndTime::durationFromSeconds(TimeTolerance);
468 DateAndTime start, stop;
470 for (
size_t i = 0; i < m_values.size(); ++i) {
471 const DateAndTime lastGoodTime = t;
473 t = m_values[i].time();
474 TYPE val = m_values[i].value();
477 const bool isGood = ((val >= min) && (val <= max));
481 if (isGood != lastGood) {
487 start = centre ? t - tol : t;
492 stop = centre ? lastGoodTime + tol : t;
493 split.emplace_back(start, stop, 0);
505 split.emplace_back(start, stop, 0);
517 "is not implemented for string "
537template <
typename TYPE>
539 const TimeInterval &expandRange,
double TimeTolerance,
bool centre,
540 const TimeROI *existingROI)
const {
541 const bool emptyMin = (min ==
EMPTY_DBL());
542 const bool emptyMax = (max ==
EMPTY_DBL());
544 if (!emptyMin && !emptyMax && max < min) {
545 std::stringstream ss;
546 ss <<
"TimeSeriesProperty::makeFilterByValue: 'max' argument must be "
547 "greater than 'min' "
548 <<
"(got min=" << min <<
" max=" << max <<
")";
549 throw std::invalid_argument(ss.str());
555 min =
static_cast<double>(minValue());
557 max =
static_cast<double>(maxValue());
562 if (m_values.empty())
569 const time_duration tol = DateAndTime::durationFromSeconds(TimeTolerance);
571 DateAndTime start, stop;
574 for (
size_t i = 0; i < m_values.size(); ++i) {
575 TYPE val = m_values[i].value();
577 if ((val >= min) && (val <= max)) {
579 stop_t = m_values[i].time();
582 stop_t = m_values[i].time();
583 start = centre ? m_values[i].time() - tol : m_values[i].time();
586 stop = centre ? stop_t + tol : m_values[i].time();
588 newROI.
addROI(start, stop);
593 stop = centre ? stop_t + tol : stop_t;
595 newROI.
addROI(start, stop);
599 if (expandRange.
start() < firstTime()) {
600 auto val =
static_cast<double>(firstValue());
601 if ((val >= min) && (val <= max)) {
605 if (expandRange.
stop() > lastTime()) {
606 auto val =
static_cast<double>(lastValue());
607 if ((val >= min) && (val <= max)) {
608 newROI.
addROI(lastTime(), expandRange.
stop());
619 if (existingROI !=
nullptr && !existingROI->
useAll()) {
634 "is not implemented for string "
648template <
typename TYPE>
651 const bool emptyMin = (min ==
EMPTY_DBL());
652 const bool emptyMax = (max ==
EMPTY_DBL());
654 if (!emptyMin && !emptyMax && max < min) {
655 std::stringstream ss;
656 ss <<
"TimeSeriesProperty::expandFilterToRange: 'max' argument must be "
657 "greater than 'min' "
658 <<
"(got min=" << min <<
" max=" << max <<
")";
659 throw std::invalid_argument(ss.str());
665 min =
static_cast<double>(minValue());
667 max =
static_cast<double>(maxValue());
669 if (range.
start() < firstTime()) {
671 double val =
static_cast<double>(firstValue());
672 if ((val >= min) && (val <= max)) {
674 extraFilter.emplace_back(range.
start(), firstTime(), 0);
681 if (lastTime() < range.
stop()) {
683 double val =
static_cast<double>(lastValue());
684 if ((val >= min) && (val <= max)) {
686 extraFilter.emplace_back(lastTime(), range.
stop(), 0);
701 "is not implemented for string "
712 if ((timeRoi ==
nullptr) || (timeRoi->
useAll())) {
714 retVal = this->averageValueInFilter(intervals);
715 }
else if (timeRoi->
useNone()) {
722 retVal = this->averageValueInFilter(filter);
723 g_log.
warning(
"Calls to TimeSeriesProperty::timeAverageValue should be replaced with "
724 "Run::getTimeAveragedValue");
726 }
catch (std::exception &) {
728 retVal = std::numeric_limits<double>::quiet_NaN();
743template <
typename TYPE>
749 return static_cast<double>(this->firstValue());
753 if (realSize() == 0 || filter.empty()) {
754 return std::numeric_limits<double>::quiet_NaN();
759 double numerator(0.0), totalTime(0.0);
761 for (
const auto &time : filter) {
763 totalTime += time.duration();
767 double currentValue =
static_cast<double>(getSingleValue(time.start(),
index));
768 DateAndTime startTime = time.start();
770 while (
index < realSize() - 1 && m_values[
index + 1].time() < time.stop()) {
772 numerator += DateAndTime::secondsFromDuration(m_values[
index].time() - startTime) * currentValue;
773 startTime = m_values[
index].time();
774 currentValue =
static_cast<double>(m_values[
index].value());
778 numerator += DateAndTime::secondsFromDuration(time.stop() - startTime) * currentValue;
783 return numerator / totalTime;
797 "averageValueInFilter is not "
798 "implemented for string properties");
801template <
typename TYPE>
802std::pair<double, double>
804 double mean_prev, mean_current(0.0), s(0.0), variance, duration, weighted_sum(0.0);
808 if (realSize() <= 1 || intervals.empty()) {
809 return std::pair<double, double>{this->averageValueInFilter(intervals), std::numeric_limits<double>::quiet_NaN()};
811 auto real_size = realSize();
812 for (
const auto &time : intervals) {
814 auto currentValue =
static_cast<double>(getSingleValue(time.start(),
index));
815 DateAndTime startTime = time.start();
816 while (
index < realSize() - 1 && m_values[
index + 1].time() < time.stop()) {
818 if (
index == real_size) {
819 duration = DateAndTime::secondsFromDuration(time.stop() - startTime);
821 duration = DateAndTime::secondsFromDuration(m_values[
index].time() - startTime);
822 startTime = m_values[
index].time();
824 mean_prev = mean_current;
826 weighted_sum += duration;
828 mean_current = mean_prev + (duration / weighted_sum) * (currentValue - mean_prev);
829 s += duration * (currentValue - mean_prev) * (currentValue - mean_current);
831 currentValue =
static_cast<double>(m_values[
index].value());
835 duration = DateAndTime::secondsFromDuration(time.stop() - startTime);
837 weighted_sum += duration;
838 mean_prev = mean_current;
840 mean_current = mean_prev + (duration / weighted_sum) * (currentValue - mean_prev);
841 s += duration * (currentValue - mean_prev) * (currentValue - mean_current);
844 variance = s / weighted_sum;
846 return std::pair<double, double>{mean_current, std::sqrt(variance)};
853std::pair<double, double>
856 "averageAndStdDevInFilter is not "
857 "implemented for string properties");
860template <
typename TYPE>
863 if (this->realSize() == 0)
864 return std::pair<double, double>{std::numeric_limits<double>::quiet_NaN(),
865 std::numeric_limits<double>::quiet_NaN()};
866 else if (this->realSize() == 1)
867 return std::pair<double, double>(
static_cast<double>(this->firstValue()), 0.0);
870 std::vector<TimeInterval> intervals;
871 if (timeRoi && !timeRoi->
useAll()) {
877 return this->averageAndStdDevInFilter(intervals);
884std::pair<double, double>
887 "TimeSeriesProperty::timeAverageValueAndStdDev is not implemented for string properties");
894#if defined(__GNUC__) && !(defined(__INTEL_COMPILER))
895#pragma GCC diagnostic warning "-Wconversion"
909 std::map<DateAndTime, TYPE> asMap;
911 if (!m_values.empty()) {
912 for (
size_t i = 0; i < m_values.size(); i++)
913 asMap[m_values[i].time()] = m_values[i].value();
926 std::vector<TYPE> out;
927 out.reserve(m_values.size());
929 for (
size_t i = 0; i < m_values.size(); i++)
930 out.emplace_back(m_values[i].value());
942 std::multimap<DateAndTime, TYPE> asMultiMap;
944 if (!m_values.empty()) {
945 for (
size_t i = 0; i < m_values.size(); i++)
946 asMultiMap.insert(std::make_pair(m_values[i].time(), m_values[i].value()));
959 std::vector<DateAndTime> out;
960 out.reserve(m_values.size());
962 for (
size_t i = 0; i < m_values.size(); i++) {
963 out.emplace_back(m_values[i].time());
978template <
typename TYPE>
980 if (roi && !roi->
useAll()) {
981 this->sortIfNecessary();
982 std::vector<DateAndTime> filteredTimes;
983 if (roi->
firstTime() > this->m_values.back().time()) {
985 filteredTimes.emplace_back(roi->
firstTime());
988 std::size_t index_current_log{0};
991 const auto endTime = splitter.stop();
994 if (endTime < this->m_values[index_current_log].time()) {
999 const auto beginTime = splitter.start();
1002 if (this->m_values.back().time() < beginTime) {
1004 index_current_log = this->m_values.size() - 1;
1007 while ((this->m_values[index_current_log].time() <= beginTime)) {
1008 if (index_current_log + 1 > this->m_values.size())
1010 index_current_log++;
1013 if (index_current_log > 0)
1014 index_current_log--;
1016 while (index_current_log > 0 &&
1017 this->m_values[index_current_log].time() == this->m_values[index_current_log - 1].time()) {
1018 index_current_log--;
1023 for (; index_current_log < this->m_values.size(); ++index_current_log) {
1024 if (this->m_values[index_current_log].time() >= endTime)
1028 filteredTimes.emplace_back(std::max(beginTime, this->m_values[index_current_log].time()));
1031 if (index_current_log > 0)
1032 index_current_log--;
1036 return filteredTimes;
1038 return this->timesAsVector();
1043 return this->timesAsVector();
1055 std::vector<double> out;
1056 if (!m_values.empty()) {
1057 out.reserve(m_values.size());
1059 Types::Core::DateAndTime start = m_values[0].time();
1060 for (
size_t i = 0; i < m_values.size(); i++) {
1061 out.emplace_back(DateAndTime::secondsFromDuration(m_values[i].time() - start));
1073template <
typename TYPE>
1079 std::vector<double> out;
1080 if (!m_values.empty()) {
1081 out.reserve(m_values.size());
1082 for (
size_t i = 0; i < m_values.size(); i++) {
1083 out.emplace_back(DateAndTime::secondsFromDuration(m_values[i].time() - start));
1095template <
typename TYPE>
1099 m_values.emplace_back(newvalue);
1126 return addValue(Types::Core::DateAndTime(time),
value);
1135 Types::Core::DateAndTime dt;
1136 dt.set_from_time_t(time);
1137 return addValue(dt,
value);
1145template <
typename TYPE>
1147 const std::vector<TYPE> &values) {
1148 size_t length = std::min(times.size(), values.size());
1149 m_size +=
static_cast<int>(length);
1150 for (
size_t i = 0; i < length; ++i) {
1151 m_values.emplace_back(times[i], values[i]);
1154 if (!values.empty())
1163template <
typename TYPE>
1165 const std::vector<TYPE> &values) {
1167 addValues(times, values);
1175 if (m_values.empty()) {
1176 const std::string
error(
"lastTime(): TimeSeriesProperty '" +
name() +
"' is empty");
1178 throw std::runtime_error(
error);
1183 return m_values.rbegin()->time();
1190 if (m_values.empty()) {
1191 const std::string
error(
"firstValue(): TimeSeriesProperty '" +
name() +
"' is empty");
1193 throw std::runtime_error(
error);
1198 return m_values[0].value();
1203 if (startTime <= this->firstTime()) {
1204 return this->firstValue();
1205 }
else if (startTime >= this->lastTime()) {
1206 return this->lastValue();
1208 const auto times = this->timesAsVector();
1209 auto iter = std::lower_bound(times.cbegin(), times.cend(), startTime);
1210 if (*iter > startTime)
1212 const auto index = std::size_t(std::distance(times.cbegin(), iter));
1214 const auto values = this->valuesAsVector();
1224 if (m_values.empty()) {
1225 const std::string
error(
"firstTime(): TimeSeriesProperty '" +
name() +
"' is empty");
1227 throw std::runtime_error(
error);
1232 return m_values[0].time();
1240 if (m_values.empty()) {
1241 const std::string
error(
"lastValue(): TimeSeriesProperty '" +
name() +
"' is empty");
1243 throw std::runtime_error(
error);
1248 return m_values.rbegin()->value();
1252 const auto stopTime = roi.
lastTime();
1253 const auto times = this->timesAsVector();
1254 if (stopTime <= times.front()) {
1255 return this->firstValue();
1256 }
else if (stopTime >= times.back()) {
1257 return this->lastValue();
1259 auto iter = std::lower_bound(times.cbegin(), times.cend(), stopTime);
1260 if ((iter != times.cbegin()) && (*iter > stopTime))
1262 const auto index = std::size_t(std::distance(times.cbegin(), iter));
1264 const auto values = this->valuesAsVector();
1280 if (this->size() == 0)
1281 return std::numeric_limits<double>::quiet_NaN();
1282 if (roi && !roi->
useAll()) {
1284 const auto thisFirstTime = this->firstTime();
1286 if (thisFirstTime > DateAndTime::GPS_EPOCH) {
1287 seriesSpan.
addMask(DateAndTime::GPS_EPOCH, thisFirstTime);
1292 const double duration_sec =
1293 std::accumulate(intervals.cbegin(), intervals.cend(), 0.,
1294 [](
double sum,
const auto &interval) { return sum + interval.duration(); });
1295 return duration_sec;
1310 return raw_stats.
mean;
1330 std::stringstream ins;
1331 for (
size_t i = 0; i < m_values.size(); i++) {
1333 ins << m_values[i].time().toSimpleString();
1334 ins <<
" " << m_values[i].value() <<
"\n";
1338 ins <<
"Error Error"
1353 std::vector<std::string> values;
1354 values.reserve(m_values.size());
1356 for (
size_t i = 0; i < m_values.size(); i++) {
1357 std::stringstream line;
1358 line << m_values[i].time().toSimpleString() <<
" " << m_values[i].value();
1359 values.emplace_back(line.str());
1378 std::map<DateAndTime, TYPE> asMap;
1379 if (m_values.empty())
1382 TYPE d = m_values[0].value();
1383 asMap[m_values[0].time()] =
d;
1385 for (
size_t i = 1; i < m_values.size(); i++) {
1386 if (m_values[i].
value() !=
d) {
1388 asMap[m_values[i].time()] = m_values[i].value();
1389 d = m_values[i].value();
1402 "Cannot extract TimeSeries from a "
1413 "Cannot extract TimeSeries from a "
1421template <
typename TYPE>
1424 "Cannot extract TimeSeries from "
1446 if (realSize() > 1) {
1447 auto lastValueInVec = m_values.back();
1449 m_values.emplace_back(lastValueInVec);
1464template <
typename TYPE>
1466 const std::vector<TYPE> &new_values) {
1467 if (time_sec.size() != new_values.size()) {
1468 std::stringstream msg;
1469 msg <<
"TimeSeriesProperty \"" <<
name() <<
"\" create: mismatched size "
1470 <<
"for the time and values vectors.";
1471 throw std::invalid_argument(msg.str());
1475 const std::size_t num = new_values.size();
1476 m_values.reserve(num);
1479 if (std::is_sorted(time_sec.cbegin(), time_sec.cend()))
1484 constexpr double SEC_TO_NANO{1000000000.0};
1485 const int64_t start_time_ns = start_time.totalNanoseconds();
1486 for (std::size_t i = 0; i < num; i++) {
1487 m_values.emplace_back(start_time_ns +
static_cast<int64_t
>(time_sec[i] * SEC_TO_NANO), new_values[i]);
1491 m_size =
static_cast<int>(m_values.size());
1503template <
typename TYPE>
1505 const std::vector<TYPE> &new_values) {
1506 if (new_times.size() != new_values.size())
1507 throw std::invalid_argument(
"TimeSeriesProperty::create: mismatched size "
1508 "for the time and values vectors.");
1512 if (new_times.empty()) {
1517 const std::size_t num = new_values.size();
1518 m_values.reserve(num);
1521 if (std::is_sorted(new_times.cbegin(), new_times.cend()))
1526 for (std::size_t i = 0; i < num; i++) {
1527 m_values.emplace_back(new_times[i], new_values[i]);
1531 m_size =
static_cast<int>(m_values.size());
1539 if (m_values.empty()) {
1540 const std::string
error(
"getSingleValue(): TimeSeriesProperty '" +
name() +
"' is empty");
1542 throw std::runtime_error(
error);
1550 if (t < m_values[0].time()) {
1552 valueAtTime = m_values[0].value();
1553 }
else if (t >= m_values.back().time()) {
1555 valueAtTime = m_values.back().value();
1558 int index = this->findIndex(t);
1563 }
else if (
index ==
int(m_values.size())) {
1565 index =
static_cast<int>(m_values.size()) - 1;
1566 }
else if (
index >
int(m_values.size())) {
1567 std::stringstream errss;
1568 errss <<
"TimeSeriesProperty.findIndex() returns index (" <<
index <<
" ) > maximum defined value "
1570 throw std::logic_error(errss.str());
1573 valueAtTime = m_values[
static_cast<size_t>(
index)].
value();
1584template <
typename TYPE>
1586 if (m_values.empty()) {
1587 const std::string
error(
"getSingleValue(): TimeSeriesProperty '" +
name() +
"' is empty");
1589 throw std::runtime_error(
error);
1597 if (t < m_values[0].time()) {
1599 valueAtTime = m_values[0].value();
1601 }
else if (t >= m_values.back().time()) {
1603 valueAtTime = m_values.back().value();
1604 index = int(m_values.size()) - 1;
1607 index = this->findIndex(t);
1612 }
else if (
index ==
int(m_values.size())) {
1614 index =
static_cast<int>(m_values.size()) - 1;
1615 }
else if (
index >
int(m_values.size())) {
1616 std::stringstream errss;
1617 errss <<
"TimeSeriesProperty.findIndex() returns index (" <<
index <<
" ) > maximum defined value "
1619 throw std::logic_error(errss.str());
1622 valueAtTime = m_values[
static_cast<size_t>(
index)].
value();
1640 if (m_values.empty()) {
1641 const std::string
error(
"nthInterval(): TimeSeriesProperty '" +
name() +
"' is empty");
1643 throw std::runtime_error(
error);
1654 if (
n >=
static_cast<int>(m_values.size()) || (
n ==
static_cast<int>(m_values.size()) - 1 && m_values.size() == 1)) {
1657 }
else if (
n ==
static_cast<int>(m_values.size()) - 1) {
1659 DateAndTime endTime = getFakeEndTime();
1664 DateAndTime startT = m_values[
static_cast<std::size_t
>(
n)].time();
1665 DateAndTime endT = m_values[
static_cast<std::size_t
>(
n) + 1].time();
1677 const auto ultimate = m_values.rbegin()->time();
1681 while (DateAndTime::secondsFromDuration(ultimate - (m_values.rbegin() + counter)->time()) == 0.) {
1686 time_duration lastDuration = m_values.rbegin()->time() - (m_values.rbegin() + counter)->time();
1689 return m_values.rbegin()->time() + lastDuration;
1701 if (m_values.empty()) {
1702 const std::string
error(
"nthValue(): TimeSeriesProperty '" +
name() +
"' is empty");
1704 throw std::runtime_error(
error);
1713 if (
static_cast<size_t>(
n) < m_values.size()) {
1714 const auto entry = m_values[
static_cast<std::size_t
>(
n)];
1715 nthValue = entry.value();
1717 const auto entry = m_values[
static_cast<std::size_t
>(
m_size) - 1];
1718 nthValue = entry.value();
1732 if (m_values.empty()) {
1733 const std::string
error(
"nthTime(): TimeSeriesProperty '" +
name() +
"' is empty");
1735 throw std::runtime_error(
error);
1738 if (n < 0 || n >=
static_cast<int>(m_values.size()))
1739 n =
static_cast<int>(m_values.size()) - 1;
1741 return m_values[
static_cast<size_t>(
n)].time();
1749 m_size = int(m_values.size());
1757 static const boost::regex re(
"^[0-9]{4}.[0-9]{2}.[0-9]{2}.[0-9]{2}.[0-9]{2}.[0-9]{2}");
1758 return boost::regex_search(str.begin(), str.end(), re);
1787template <
typename TYPE>
1791 out.
duration = this->durationInSeconds(roi);
1798 auto avAndDev = this->timeAverageValueAndStdDev(roi);
1819template <
typename TYPE>
1821 using namespace Kernel::Math;
1822 double singleValue = 0;
1823 switch (selection) {
1825 if (roi && !roi->
useAll())
1826 singleValue = double(this->firstValue(*roi));
1828 singleValue = double(this->nthValue(0));
1831 if (roi && !roi->
useAll())
1832 singleValue = double(this->lastValue(*roi));
1834 singleValue = double(this->nthValue(this->size() - 1));
1848 case TimeAveragedMean:
1854 case TimeAverageStdDev:
1855 singleValue = this->
getStatistics(roi).time_standard_deviation;
1858 throw std::invalid_argument(
"extractStatistic - Unknown statistic type: " + boost::lexical_cast<std::string>(
this));
1871 "extractStatistic is not "
1872 "implemented for string properties");
1884 const auto origSize{
m_size};
1889 auto it = std::unique(m_values.rbegin(), m_values.rend(),
1890 [](
const auto &a,
const auto &b) { return a.time() == b.time(); });
1891 m_values.erase(m_values.begin(), it.base());
1897 const auto numremoved = origSize -
m_size;
1899 g_log.
notice() <<
"Log \"" << this->
name() <<
"\" has " << numremoved <<
" entries removed due to duplicated time. "
1907 std::stringstream ss;
1908 for (
size_t i = 0; i < m_values.size(); ++i)
1909 ss << m_values[i].time() <<
"\t\t" << m_values[i].value() <<
"\n";
1925 bool sorted = is_sorted(m_values.begin(), m_values.end());
1934 <<
"\" is not sorted. Sorting is operated on it. \n";
1935 std::stable_sort(m_values.begin(), m_values.end());
1948 if (m_values.empty())
1955 if (t <= m_values[0].time()) {
1957 }
else if (t >= m_values.back().time()) {
1958 return (
int(m_values.size()));
1962 typename std::vector<TimeValueUnit<TYPE>>::const_iterator fid;
1964 fid = std::lower_bound(m_values.begin(), m_values.end(), temp);
1966 int newindex = int(fid - m_values.begin());
1967 if (fid->time() > t)
1979template <
typename TYPE>
1983 throw std::invalid_argument(
"Start Index cannot be less than 0");
1985 if (iend >=
static_cast<int>(m_values.size())) {
1986 throw std::invalid_argument(
"End Index cannot exceed the boundary");
1988 if (istart > iend) {
1989 throw std::invalid_argument(
"Start index cannot be greater than end index");
1993 if (t < (m_values.begin() + istart)->time()) {
1996 if (t > (m_values.begin() + iend)->time()) {
1997 return static_cast<int>(m_values.size());
2005 const auto first = m_values.cbegin() + istart;
2006 const auto last = m_values.cbegin() + iend + 1;
2007 const auto iter = std::lower_bound(first, last, temppair);
2011 throw std::runtime_error(
"Cannot find data");
2012 return static_cast<int>(std::distance(m_values.cbegin(), iter));
2025 return "Could not set value: properties have different type.";
2029 m_propSortedFlag = prop->m_propSortedFlag;
2039 std::vector<DateAndTime> times = this->timesAsVector();
2040 const DateAndTime &start = times.front();
2041 std::vector<double> timeSec(times.size());
2042 for (
size_t i = 0; i < times.size(); i++)
2043 timeSec[i] =
static_cast<double>(times[i].totalNanoseconds() - start.totalNanoseconds()) * 1e-9;
2044 file->writeData(
"time", timeSec);
2045 file->openData(
"time");
2046 file->putAttr(
"start", start.toISO8601String());
2053 std::vector<std::string> values = this->valuesAsVector();
2056 file->makeGroup(this->
name(),
"NXlog",
true);
2059 auto max_it = std::max_element(values.begin(), values.end(),
2060 [](
const std::string &a,
const std::string &b) { return a.size() < b.size(); });
2062 size_t maxlen = max_it->size() + 1;
2064 std::vector<char> strs(values.size() * maxlen);
2066 for (
const auto &prop : values) {
2067 std::copy(prop.begin(), prop.end(), &strs[
index]);
2073 file->putData(strs.data());
2075 saveTimeVector(file);
2086 std::vector<bool>
value = this->valuesAsVector();
2089 std::vector<uint8_t> asUint(
value.begin(),
value.end());
2090 file->makeGroup(this->
name(),
"NXlog",
true);
2091 file->writeData(
"value", asUint);
2092 file->putAttr(
"boolean",
"1");
2093 saveTimeVector(file);
2098 auto values = this->valuesAsVector();
2101 file->makeGroup(this->
name(),
"NXlog", 1);
2102 file->writeData(
"value", values);
2103 file->openData(
"value");
2104 file->putAttr(
"units", this->units());
2106 saveTimeVector(file);
2126template <
typename TYPE>
2128 std::vector<double> &counts)
const {
2130 size_t nPoints = counts.size();
2134 auto t0 =
static_cast<double>(tMin.totalNanoseconds());
2135 auto t1 =
static_cast<double>(tMax.totalNanoseconds());
2137 throw std::invalid_argument(
"invalid arguments for histogramData; tMax<tMin");
2139 double dt = (t1 - t0) /
static_cast<double>(nPoints);
2141 for (
auto const &ev : m_values) {
2142 auto time =
static_cast<double>(ev.time().totalNanoseconds());
2143 if (time < t0 || time >= t1)
2145 auto ind =
static_cast<size_t>((time - t0) / dt);
2146 counts[ind] +=
static_cast<double>(ev.value());
2152 const Types::Core::DateAndTime &tMax,
2153 std::vector<double> &counts)
const {
2157 throw std::runtime_error(
"histogramData is not implememnted for time series "
2158 "properties containing strings");
2171 if (roi && !roi->
useAll()) {
2172 this->sortIfNecessary();
2173 std::vector<TYPE> filteredValues;
2174 if (roi->
firstTime() > this->m_values.back().time()) {
2176 filteredValues.emplace_back(this->m_values.back().value());
2179 std::size_t index_current_log{0};
2181 const auto endTime = splitter.stop();
2184 if (endTime < this->m_values[index_current_log].time()) {
2189 const auto beginTime = splitter.start();
2192 if (this->m_values.back().time() < beginTime) {
2194 index_current_log = this->m_values.size() - 1;
2197 while ((this->m_values[index_current_log].time() <= beginTime)) {
2198 if (index_current_log + 1 > this->m_values.size())
2200 index_current_log++;
2203 if (index_current_log > 0)
2204 index_current_log--;
2206 while (index_current_log > 0 &&
2207 this->m_values[index_current_log].time() == this->m_values[index_current_log - 1].time()) {
2208 index_current_log--;
2213 for (; index_current_log < this->m_values.size(); ++index_current_log) {
2214 if (this->m_values[index_current_log].time() >= endTime)
2218 filteredValues.emplace_back(this->m_values[index_current_log].
value());
2221 if (index_current_log > 0)
2222 index_current_log--;
2225 return filteredValues;
2227 return this->valuesAsVector();
2232 return this->valuesAsVector();
2244 std::vector<TimeInterval> intervals;
2245 auto lastInterval = this->nthInterval(this->size() - 1);
2246 intervals.emplace_back(firstTime(), lastInterval.stop());
2253#define INSTANTIATE(TYPE) template class TimeSeriesProperty<TYPE>;
const std::vector< double > & rhs
size_t m_size
Maximum size of the store.
double value
The value of the point.
std::map< DeltaEMode::Type, std::string > index
#define INSTANTIATE(TYPE)
boost::python::list getTimeIntervals(const TimeROI &self)
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Marks code as not implemented yet.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
Base class for properties.
const std::string & name() const
Get the property's name.
Represents a time interval.
const Types::Core::DateAndTime & start() const
Beginning of the interval.
const Types::Core::DateAndTime & stop() const
End of the interval.
TimeROI : Object that holds information about when the time measurement was active.
double durationInSeconds() const
Duration of the whole TimeROI.
void addMask(const std::string &startTime, const std::string &stopTime)
const std::vector< Kernel::TimeInterval > toTimeIntervals() const
This method is to lend itself to helping with transition.
const std::vector< Types::Core::DateAndTime > & getAllTimes() const
Types::Core::DateAndTime firstTime() const
void addROI(const std::string &startTime, const std::string &stopTime)
bool useNone() const
TimeROI selects no time to be used as all is invalid.
bool useAll() const
TimeROI selects all time to be used.
void update_intersection(const TimeROI &other)
Updates the TimeROI values with the intersection with another TimeROI.
Types::Core::DateAndTime lastTime() const
static const TimeROI USE_NONE
Constant for TimeROI where no time is used.
A specialised Property class for holding a series of time-value pairs.
void replaceValues(const std::vector< Types::Core::DateAndTime > ×, const std::vector< TYPE > &values)
Replaces the time series with new values time series values.
TimeSeriesProperty< TYPE > * clone() const override
"Virtual" copy constructor
std::string getDefault() const override
Returns the default value.
std::map< Types::Core::DateAndTime, TYPE > valueAsMap() const
Return the time series as a C++ map<DateAndTime, TYPE>
size_t getMemorySize() const override
Return the memory used by the property, in bytes.
Json::Value valueAsJson() const override
std::string toString() const
Stringize the property.
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>
Types::Core::DateAndTime firstTime() const
Returns the first time regardless of filter.
double durationInSeconds(const Kernel::TimeROI *roi=nullptr) const
Returns the duration of the time series, possibly restricted by a TimeROI object.
std::pair< double, double > timeAverageValueAndStdDev(const Kernel::TimeROI *timeRoi=nullptr) const override
Returns the calculated time weighted mean and standard deviation values.
void eliminateDuplicates()
Detects whether there are duplicated entries (of time) in property & eliminates them.
std::vector< double > timesAsVectorSeconds() const
Return the series as list of times, where the time is the number of seconds since the start.
std::multimap< Types::Core::DateAndTime, TYPE > valueAsMultiMap() const
Return the time series as a correct C++ multimap<DateAndTime, TYPE>.
TimeSeriesProperty & operator+=(Property const *right) override
Add the value of another property.
TimeSeriesProperty< TYPE > & merge(Property *rhs) override
Merge the given property with this one.
std::string value() const override
Get the time series property as a string of 'time value'.
void createFilteredData(const TimeROI &timeROI, std::vector< TimeValueUnit< TYPE > > &filteredData) const
Fill in the supplied vector of time series data according to the input TimeROI.
Property * cloneInTimeROI(const TimeROI &timeROI) const override
Create a partial copy according to TimeROI.
void makeFilterByValue(std::vector< SplittingInterval > &split, double min, double max, double TimeTolerance=0.0, bool centre=false) const override
Fill a SplittingIntervalVec that will filter the events by matching.
bool isDefault() const override
Returns if the value is at the default.
void sortIfNecessary() const
Sort the property into increasing times, if not already sorted.
double mean() const
Returns the mean value found in the series.
TimeSeriesPropertyStatistics getStatistics(const Kernel::TimeROI *roi=nullptr) const override
Return a TimeSeriesPropertyStatistics object.
int findIndex(Types::Core::DateAndTime t) const
Find the index of the entry of time t in the mP vector (sorted)
TYPE firstValue() const
Returns the first value regardless of filter.
void saveTimeVector(Nexus::File *file)
Saves the time vector has time + start attribute.
virtual bool operator!=(const TimeSeriesProperty< TYPE > &right) const
Deep comparison (not equal).
void histogramData(const Types::Core::DateAndTime &tMin, const Types::Core::DateAndTime &tMax, std::vector< double > &counts) const
generate constant time-step histogram from the property values
void expandFilterToRange(std::vector< SplittingInterval > &split, double min, double max, const TimeInterval &range) const override
Make sure an existing filter covers the full time range given.
~TimeSeriesProperty() override
Virtual destructor.
double timeAverageValue(const TimeROI *timeRoi=nullptr) const override
Returns the calculated time weighted average value.
std::string setValue(const std::string &) override
Set a property from a string.
virtual std::vector< Types::Core::DateAndTime > filteredTimesAsVector() const
void addValue(const Types::Core::DateAndTime &time, const TYPE &value)
Add a value to the map using a DateAndTime object.
int m_size
The number of values (or time intervals) in the time series.
TimeSeriesProperty(const std::string &name)
Constructor.
void removeDataOutsideTimeROI(const TimeROI &timeRoi) override
Remove time values outside of TimeROI regions each defined as [roi_begin,roi_end].
void countSize() const
Updates size()
void clear() override
Deletes the series of values in the property.
Types::Core::DateAndTime lastTime() const
Returns the last time.
void setName(const std::string &name)
Set name of property.
TYPE lastValue() const
Returns the last value.
double extractStatistic(Math::StatisticType selection, const TimeROI *roi=nullptr) const override
Calculate a particular statistical quantity from the values of the time series.
std::map< Types::Core::DateAndTime, TYPE > valueAsCorrectMap() const
Return the time series as a correct C++ map<DateAndTime, TYPE>.
std::string setValueFromJson(const Json::Value &) override
Set a property from a string.
std::unique_ptr< TimeSeriesProperty< double > > getDerivative() const
Return time series property, containing time derivative of current property.
std::vector< TimeValueUnit< TYPE > > m_values
Holds the time series data.
void create(const Types::Core::DateAndTime &start_time, const std::vector< double > &time_sec, const std::vector< TYPE > &new_values)
Clears and creates a TimeSeriesProperty from these parameters.
Types::Core::DateAndTime getFakeEndTime() const
Returns an end time that will have the same spacing to the right of the last value as the last non-ze...
virtual bool operator==(const TimeSeriesProperty< TYPE > &right) const
Deep comparison.
double averageValueInFilter(const std::vector< TimeInterval > &filter) const
Calculate the time-weighted average of a property in a filtered range.
virtual std::vector< Mantid::Kernel::TimeInterval > getTimeIntervals() const
If filtering by log, get the time intervals for splitting.
void clearOutdated() override
Deletes all but the 'last entry' in the property.
virtual TYPE nthValue(int n) const
Returns n-th value of n-th interval in an incredibly inefficient way.
std::string setDataItem(const std::shared_ptr< DataItem > &) override
Set a property from a DataItem.
std::string isValid() const override
This doesn't check anything -we assume these are always valid.
std::vector< std::string > time_tValue() const
New method to return time series value pairs as std::vector<std::string>
std::vector< Types::Core::DateAndTime > timesAsVector() const override
Return the time series's times as a vector<DateAndTime>
std::pair< double, double > averageAndStdDevInFilter(const std::vector< TimeInterval > &intervals) const
Calculate the time-weighted average and std-deviation of a property in a filtered range.
virtual Types::Core::DateAndTime nthTime(int n) const
Returns n-th time. NOTE: Complexity is order(n)! regardless of filter.
TYPE getSingleValue(const Types::Core::DateAndTime &t) const
Returns the value at a particular time.
int upperBound(Types::Core::DateAndTime t, int istart, int iend) const
Find the upper_bound of time t in container.
virtual TimeInterval nthInterval(int n) const
Returns n-th valid time interval, in a very inefficient way.
void addValues(const std::vector< Types::Core::DateAndTime > ×, const std::vector< TYPE > &values)
Adds vectors of values to the map.
int realSize() const override
Returns the real size of the time series property map:
static bool isTimeString(const std::string &str)
Check if str has the right time format.
Property * cloneWithTimeShift(const double timeShift) const override
"Virtual" copy constructor with a time shift in seconds
std::string setValueFromProperty(const Property &right) override
Set a value from another property.
virtual std::vector< TYPE > filteredValuesAsVector() const
void saveProperty(Nexus::File *file) override
Class to hold unit value (DateAndTime, T)
static unsigned short constexpr CHAR
MatrixWorkspace_sptr MANTID_API_DLL operator+=(const MatrixWorkspace_sptr &lhs, const MatrixWorkspace_sptr &rhs)
Adds two workspaces.
void split(const int A, int &S, int &V)
Split a number into the sign and positive value.
Logger g_log("DateAndTime")
StatisticType
Maps a "statistic" to a number.
Statistics getStatistics(const std::vector< TYPE > &data, const unsigned int flags=StatOptions::AllStats)
Return a statistics object for the given data set.
std::vector< SplittingInterval > SplittingIntervalVec
A typedef for splitting events according their pulse time.
MANTID_KERNEL_DLL bool operator==(const Mantid::Kernel::Property &lhs, const Mantid::Kernel::Property &rhs)
Compares this to another property for equality.
std::vector< dimsize_t > DimVector
Helper class which provides the Collimation Length for SANS instruments.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Simple struct to store statistics.
double median
Median value.
double minimum
Minimum value.
double maximum
Maximum value.
double standard_deviation
standard_deviation of the values
Struct holding some useful statistics for a TimeSeriesProperty.
double time_standard_deviation
time weighted standard deviation
double standard_deviation
standard_deviation of the values
double time_mean
time weighted average
double duration
Duration in seconds.