Mantid
Loading...
Searching...
No Matches
ExportTimeSeriesLog.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include "MantidAPI/Axis.h"
11#include "MantidAPI/Run.h"
18#include "MantidHistogramData/Histogram.h"
20#include "MantidKernel/System.h"
23#include <algorithm>
24#include <fstream>
25#include <sstream>
26
27using namespace Mantid;
28using namespace Mantid::Kernel;
29using namespace Mantid::API;
30using namespace Mantid::DataObjects;
31using namespace Mantid::HistogramData;
32using Mantid::Types::Core::DateAndTime;
33
34using namespace std;
35
36namespace Mantid::Algorithms {
37
38DECLARE_ALGORITHM(ExportTimeSeriesLog)
39
40
42void ExportTimeSeriesLog::init() {
43 declareProperty(
44 std::make_unique<API::WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "Anonymous", Direction::InOut),
45 "Name of input Matrix workspace containing the log to export. ");
46
47 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "Dummy", Direction::Output),
48 "Name of the workspace containing the log events in Export. ");
49
50 declareProperty("CalculateFirstDerivative", false,
51 "If specified then the first derivative of exported data "
52 "will be calcualted and put to spectrum 1.");
53
54 declareProperty("LogName", "", "Log's name to filter events.");
55
56 std::vector<std::string> units{"Seconds", "Nano Seconds"};
57 declareProperty("UnitOfTime", "Seconds", std::make_shared<Kernel::StringListValidator>(units),
58 "StartTime, StopTime and DeltaTime can be given in various unit."
59 "The unit can be 'Seconds' or 'Nanoseconds' from run start time."
60 "They can also be defined as 'Percentage' of total run time.");
61
62 declareProperty("StartTime", EMPTY_DBL(),
63 "Relative starting time of the output series. "
64 "Its unit is determined by property UnitOfTime.");
65
66 declareProperty("StopTime", EMPTY_DBL(),
67 "Relative stopping time of the output series."
68 "Its unit is determined by property UnitOfTime.");
69
70 declareProperty("OutputAbsoluteTime", false, "If true, the output times will be absolute time to 1990.01.01.");
71
72 declareProperty("NumberEntriesExport", EMPTY_INT(),
73 "Number of entries of the log to be exported. Default is all entries.");
74
75 declareProperty("IsEventWorkspace", true,
76 "If set to true, output workspace "
77 "is EventWorkspace. Otherwise, it "
78 "is Workspace2D.");
79}
80
84 // Get properties
85 m_inputWS = this->getProperty("InputWorkspace");
86 string logname = getProperty("LogName");
87
88 double start_time = getProperty("StartTime");
89 double stop_time = getProperty("StopTime");
90 std::string time_unit = getProperty("UnitOfTime");
91 bool exportEpochTime = getProperty("OutputAbsoluteTime");
92
93 int numberoutputentries = getProperty("NumberEntriesExport");
94 bool outputeventworkspace = getProperty("IsEventWorkspace");
95
96 bool cal1stderiv = getProperty("CalculateFirstDerivative");
97
98 // Call the main
99 exportLog(logname, time_unit, start_time, stop_time, exportEpochTime, outputeventworkspace, numberoutputentries,
100 cal1stderiv);
101
102 // calcualte first derivative
103 if (cal1stderiv)
104 calculateFirstDerivative(outputeventworkspace);
105
106 // set up the sample log values for meta information
107 setupMetaData(logname, time_unit, exportEpochTime);
108
109 // 3. Output
110 setProperty("OutputWorkspace", m_outWS);
111}
112
124void ExportTimeSeriesLog::exportLog(const std::string &logname, const std::string &timeunit, const double &starttime,
125 const double &stoptime, const bool exportepoch, bool outputeventws, int numentries,
126 bool cal_first_deriv) {
127
128 // Get log, time, and etc.
129 std::vector<Types::Core::DateAndTime> times;
130 std::vector<double> values;
131
132 if (!logname.empty()) {
133 // Log
134 auto *tlog = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(m_inputWS->run().getProperty(logname));
135 if (!tlog) {
136 std::stringstream errmsg;
137 errmsg << "TimeSeriesProperty Log " << logname << " does not exist in workspace " << m_inputWS->getName();
138 g_log.error(errmsg.str());
139 throw std::invalid_argument(errmsg.str());
140 }
141 times = tlog->timesAsVector();
142 values = tlog->valuesAsVector();
143 } else {
144 throw std::runtime_error("Log name cannot be left empty.");
145 }
146
147 // Get start time, stop time and unit factor
148 double timeunitfactor = 1.;
149 if (timeunit == "Seconds")
150 timeunitfactor = 1.E-9;
151
152 // Get index for start time
153 size_t i_start = 0;
154 size_t i_stop = times.size() - 1;
155 // Rule out the case that start time is behind last log entry
156 bool i_start_cal = false;
157 if (starttime != EMPTY_DBL()) {
158 int64_t timerangens = times.back().totalNanoseconds() - times.front().totalNanoseconds();
159 double timerange = static_cast<double>(timerangens) * timeunitfactor;
160 g_log.debug() << "Time range is " << timerange << ", Start time is " << starttime << "\n";
161 if (timerange < starttime) {
162 i_start = times.size() - 1;
163 i_start_cal = true;
164 }
165 }
166
167 if ((!i_start_cal) && (starttime != EMPTY_DBL() || stoptime != EMPTY_DBL())) {
168 bool export_partial = calculateTimeSeriesRangeByTime(times, starttime, i_start, stoptime, i_stop, timeunitfactor);
169 if (!export_partial)
170 throw std::runtime_error("Unable to find proton_charge for run start time. "
171 "Failed to get partial time series.");
172 }
173
174 // Determine number of export log
175 if (numentries == EMPTY_INT()) {
176 numentries = static_cast<int>(times.size());
177 } else if (numentries <= 0) {
178 stringstream errmsg;
179 errmsg << "For Export Log, NumberEntriesExport must be greater than 0. "
180 "Input = "
181 << numentries;
182 g_log.error(errmsg.str());
183 throw std::runtime_error(errmsg.str());
184 } else if (static_cast<size_t>(numentries) > times.size()) {
185 numentries = static_cast<int>(times.size());
186 }
187
188 // Create otuput workspace
189 if (outputeventws) {
190 setupEventWorkspace(i_start, i_stop, numentries, times, values, exportepoch);
191 } else {
192 size_t nspec(1);
193 if (cal_first_deriv)
194 nspec = 2;
195 setupWorkspace2D(i_start, i_stop, numentries, times, values, exportepoch, timeunitfactor, nspec);
196 }
197}
198
211void ExportTimeSeriesLog::setupWorkspace2D(const size_t &start_index, const size_t &stop_index, int numentries,
212 vector<DateAndTime> &times, vector<double> values, const bool &epochtime,
213 const double &timeunitfactor, size_t nspec) {
214 // Determine time shift
215 int64_t timeshift(0);
216 if (!epochtime) {
217 // relative time
218 Types::Core::DateAndTime runstart(m_inputWS->run().getProperty("run_start")->value());
219 timeshift = runstart.totalNanoseconds();
220 }
221
222 // Determine the size
223 size_t outsize = stop_index - start_index + 1;
224 if (outsize > static_cast<size_t>(numentries))
225 outsize = static_cast<size_t>(numentries);
226
227 // Create 2D workspace
228 m_outWS = create<Workspace2D>(nspec, Points(outsize));
229
230 auto &vecX = m_outWS->mutableX(0);
231 auto &vecY = m_outWS->mutableY(0);
232 auto &vecE = m_outWS->mutableE(0);
233
234 for (size_t index = 0; index < outsize; ++index) {
235 size_t i_time = index + start_index;
236 // safety check
237 if (i_time >= times.size()) {
238 std::stringstream errss;
239 errss << "It shouldn't happen that the index is out of boundary."
240 << "start index = " << start_index << ", output size = " << outsize << ", index = " << index << "\n";
241 throw std::runtime_error(errss.str());
242 }
243
244 int64_t dtns = times[i_time].totalNanoseconds() - timeshift;
245 vecX[index] = static_cast<double>(dtns) * timeunitfactor;
246 vecY[index] = values[i_time];
247 vecE[index] = 0.0;
248 }
249
250 Axis *xaxis = m_outWS->getAxis(0);
251 xaxis->setUnit("Time");
252}
253
263void ExportTimeSeriesLog::setupEventWorkspace(const size_t &start_index, const size_t &stop_index, int numentries,
264 vector<DateAndTime> &times, vector<double> values,
265 const bool &epochtime) {
266 Types::Core::DateAndTime runstart(m_inputWS->run().getProperty("run_start")->value());
267
268 // Get some stuff from the input workspace
269 const size_t numberOfSpectra = 1;
270 // determine output size
271 size_t outsize = stop_index - start_index + 1;
272 if (outsize > static_cast<size_t>(numentries))
273 outsize = static_cast<size_t>(numentries);
274
275 std::shared_ptr<EventWorkspace> outEventWS =
276 create<EventWorkspace>(*m_inputWS, numberOfSpectra, HistogramData::BinEdges(2));
277 m_outWS = outEventWS;
278
279 // Create the output event list (empty)
280 EventList &outEL = outEventWS->getSpectrum(0);
282
283 // Allocate all the required memory
284 outEL.reserve(outsize);
285 outEL.clearDetectorIDs();
286
287 int64_t time_shift_ns(0);
288 if (!epochtime) {
289 // relative time
290 time_shift_ns = runstart.totalNanoseconds();
291 }
292
293 for (size_t i = 0; i < outsize; i++) {
294 Types::Core::DateAndTime tnow = times[i + start_index];
295 int64_t dt = tnow.totalNanoseconds() - time_shift_ns;
296
297 // convert to microseconds
298 double dtmsec = static_cast<double>(dt) / 1000.0;
299 outEL.addEventQuickly(WeightedEventNoTime(dtmsec, values[i + start_index], values[i + start_index]));
300 }
301 // Ensure thread-safety
302 outEventWS->sortAll(TOF_SORT, nullptr);
303
304 // Now, create a default X-vector for histogramming, with just 2 bins.
305 std::vector<WeightedEventNoTime> &events = outEL.getWeightedEventsNoTime();
306 outEventWS->setBinEdges(0, HistogramData::BinEdges{events.begin()->tof(), events.rbegin()->tof()});
307}
308
319bool ExportTimeSeriesLog::calculateTimeSeriesRangeByTime(std::vector<Types::Core::DateAndTime> &vec_times,
320 const double &rel_start_time, size_t &i_start,
321 const double &rel_stop_time, size_t &i_stop,
322 const double &time_factor) {
323 // Initialize if there is something wrong.
324 i_start = 0;
325 i_stop = vec_times.size() - 1;
326
327 // Check existence of proton_charge as run start
328 Types::Core::DateAndTime run_start(0);
329 if (m_inputWS->run().hasProperty("proton_charge")) {
330 auto ts = dynamic_cast<TimeSeriesProperty<double> *>(m_inputWS->run().getProperty("proton_charge"));
331 if (nullptr == ts) {
332 throw std::runtime_error("Found the run property proton_charge but failed to interpret it "
333 "as a time series property of double values (failed dynamic cast).");
334 }
335 run_start = ts->nthTime(0);
336 } else {
337 g_log.warning("Property proton_charge does not exist so it is unable to "
338 "determine run start time. "
339 "StartTime and StopTime are ignored. TimeSeriesProperty is "
340 "exported in full length.");
341 return false;
342 }
343
344 // Get time 0
345 if (rel_start_time != EMPTY_DBL()) {
346 int64_t start_time_ns = run_start.totalNanoseconds() + static_cast<int64_t>(rel_start_time / time_factor);
347 Types::Core::DateAndTime start_time(start_time_ns);
348 i_start = static_cast<size_t>(std::lower_bound(vec_times.begin(), vec_times.end(), start_time) - vec_times.begin());
349
350 // try to record the log value before starting time
351 if (i_start >= 1)
352 --i_start;
353 }
354
355 if (rel_stop_time != EMPTY_DBL()) {
356 int64_t stop_time_ns = run_start.totalNanoseconds() + static_cast<int64_t>(rel_stop_time / time_factor);
357 Types::Core::DateAndTime stop_time(stop_time_ns);
358 i_stop = static_cast<size_t>(std::lower_bound(vec_times.begin(), vec_times.end(), stop_time) - vec_times.begin());
359 }
360
361 return true;
362}
363
365 if (is_event_ws) {
366 g_log.error("It is not supported to calculate first derivative if the "
367 "output is an EventWorkspace.");
368 return;
369 }
370
371 // calcualte output
372 size_t datasize = m_outWS->mutableX(1).size();
373 auto vecX = m_outWS->mutableX(0);
374 auto vecY = m_outWS->mutableY(0);
375 auto &derivX = m_outWS->mutableX(1);
376 auto &derivY = m_outWS->mutableY(1);
377 if (vecY.size() != datasize)
378 throw std::runtime_error("Output workspace 2D is not supposed to have "
379 "different size of X and Y.");
380
381 std::stringstream errmsg_ss;
382 for (size_t i = 1; i < datasize - 1; ++i) {
383 // set up X
384 derivX[i] = vecX[i];
385 // set up Y
386 double dx = vecX[i + 1] - vecX[i];
387 if (dx <= 0) {
388 errmsg_ss << "Entry " << i << ": " << vecX[i] << " >= " << vecX[i + 1] << "\n";
389 derivY[i] = 1.;
390 } else {
391 derivY[i] = (vecY[i + 1] - vecY[i]) / dx;
392 }
393 }
394
395 // last value
396 derivX[datasize - 1] = vecX[datasize - 1];
397 derivY.back() = 0.;
398
399 // error message
400 std::string errmsg = errmsg_ss.str();
401 if (!errmsg.empty())
402 g_log.error(errmsg);
403
404 return;
405}
406
415void ExportTimeSeriesLog::setupMetaData(const std::string &log_name, const std::string &time_unit,
416 const bool &export_epoch) {
417
418 m_outWS->mutableRun().addProperty("SampleLogName", log_name, true);
419 m_outWS->mutableRun().addProperty("TimeUnit", time_unit, true);
420
421 std::string is_epoch("0");
422 if (export_epoch)
423 is_epoch = "1";
424 m_outWS->mutableRun().addProperty("IsEpochTime", is_epoch, true);
425}
426
427} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
Class to represent the axis of a workspace.
Definition: Axis.h:30
virtual const std::shared_ptr< Kernel::Unit > & setUnit(const std::string &unitName)
Set the unit on the Axis.
Definition: Axis.cpp:39
void clearDetectorIDs()
Clear the detector IDs set.
Definition: ISpectrum.cpp:117
A property class for workspaces.
ExportTimeSeriesLog : Read a TimeSeries log and return some information required by users.
void setupMetaData(const std::string &log_name, const std::string &time_unit, const bool &export_epoch)
Set up the meta data such as sample log name, unit of time, whether the time is epoch to the output w...
void setupWorkspace2D(const size_t &start_index, const size_t &stop_index, int numentries, std::vector< Types::Core::DateAndTime > &times, std::vector< double > values, const bool &epochtime, const double &timeunitfactor, size_t nspec)
Set up the output workspace in a Workspace2D.
void setupEventWorkspace(const size_t &start_index, const size_t &stop_index, int numentries, std::vector< Types::Core::DateAndTime > &times, std::vector< double > values, const bool &epochtime)
Set up an Event workspace.
bool calculateTimeSeriesRangeByTime(std::vector< Types::Core::DateAndTime > &vec_times, const double &rel_start_time, size_t &i_start, const double &rel_stop_time, size_t &i_stop, const double &time_factor)
Calculate the range of time vector by start time and stop time.
void exportLog(const std::string &logname, const std::string &timeunit, const double &starttime, const double &stoptime, const bool exportepoch, bool outputeventws, int numentries, bool cal_first_deriv)
Export part of designated log to an file in column format and a output file.
A class for holding :
Definition: EventList.h:56
std::vector< WeightedEventNoTime > & getWeightedEventsNoTime()
Return the list of WeightedEvent contained.
Definition: EventList.cpp:823
void reserve(size_t num) override
Reserve a certain number of entries in event list of the specified eventType.
Definition: EventList.cpp:895
void switchTo(Mantid::API::EventType newType) override
Switch the EventList to use the given EventType (TOF, WEIGHTED, or WEIGHTED_NOTIME)
Definition: EventList.cpp:649
void addEventQuickly(const Types::Event::TofEvent &event)
Append an event to the histogram, without clearing the cache, to make it faster.
Definition: EventList.h:104
Info about a single neutron detection event, including a weight and error value, but excluding the pu...
Definition: Events.h:91
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
A specialised Property class for holding a series of time-value pairs.
@ WEIGHTED_NOTIME
Definition: IEventList.h:18
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:25
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
STL namespace.
@ InOut
Both an input & output workspace.
Definition: Property.h:55
@ Output
An output workspace.
Definition: Property.h:54