Mantid
Loading...
Searching...
No Matches
SumEventsByLogValue.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
9#include "MantidAPI/Axis.h"
10#include "MantidAPI/Column.h"
12#include "MantidAPI/Run.h"
22
23#include <numeric>
24
25namespace Mantid::Algorithms {
26// Register the class into the algorithm factory
27DECLARE_ALGORITHM(SumEventsByLogValue)
28
29using namespace Kernel;
30using namespace API;
31using DataObjects::EventWorkspace;
34
37 std::make_unique<WorkspaceProperty<DataObjects::EventWorkspace>>("InputWorkspace", "", Direction::Input),
38 "The input EventWorkspace. Must contain 'raw' (unweighted) events");
40 "MonitorWorkspace", "", Direction::Input, PropertyMode::Optional),
41 "A workspace containing the monitor counts relating to the input "
42 "workspace");
43 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output),
44 "The name of the workspace to be created as the output of "
45 "the algorithm. The output workspace will be a "
46 "[[TableWorkspace]] in the case that a log holding integer "
47 "values is given, and a single-spectrum [[Workspace2D]] "
48 "otherwise.");
49
50 declareProperty("LogName", "", std::make_shared<MandatoryValidator<std::string>>(),
51 "The name of the number series log against which the data "
52 "should be summed");
54 std::make_unique<ArrayProperty<double>>("OutputBinning", "", std::make_shared<RebinParamsValidator>(true)),
55 "Binning parameters for the output workspace (see [[Rebin]] for syntax) "
56 "(Optional for logs holding integer values, mandatory otherwise)");
57}
58
59std::map<std::string, std::string> SumEventsByLogValue::validateInputs() {
60 std::map<std::string, std::string> errors;
61
62 // check for null pointers - this is to protect against workspace groups
63 m_inputWorkspace = getProperty("InputWorkspace");
64 if (!m_inputWorkspace) {
65 return errors;
66 }
67
68 // This only works for unweighted events
69 // TODO: Either turn this check into a proper validator or amend the algorithm
70 // to work for weighted events
71 if (m_inputWorkspace->getEventType() != API::TOF) {
72 errors["InputWorkspace"] = "This algorithm only works for unweighted ('raw') events";
73 }
74
75 // Check that the log exists for the given input workspace
76 m_logName = getPropertyValue("LogName");
77 try {
78 auto *log = dynamic_cast<ITimeSeriesProperty *>(m_inputWorkspace->run().getLogData(m_logName));
79 if (log == nullptr) {
80 errors["LogName"] = "'" + m_logName + "' is not a time-series log.";
81 return errors;
82 }
83 if (log->realSize() == 0) {
84 errors["LogName"] = "'" + m_logName + "' is empty.";
85 }
86 } catch (Exception::NotFoundError &) {
87 errors["LogName"] =
88 "The log '" + m_logName + "' does not exist in the workspace '" + m_inputWorkspace->getName() + "'.";
89 return errors;
90 }
91
92 return errors;
93}
94
96 // Get hold of the requested log. Will throw if it doesn't exist (which is
97 // what we want).
98 const Property *const log = m_inputWorkspace->run().getLogData(m_logName);
99
100 // Now we need to know what type of property it is
101 const auto *intLog = dynamic_cast<const TimeSeriesProperty<int> *>(log);
102 const auto *dblLog = dynamic_cast<const TimeSeriesProperty<double> *>(log);
103
104 m_binningParams = getProperty("OutputBinning");
105 // Binning parameters must be provided for floating point logs
106 if (m_binningParams.empty()) {
107 if (intLog != nullptr) {
108 createTableOutput(intLog);
109 } else {
110 throw std::invalid_argument("OutputBinning must be provided for floating-point number logs");
111 }
112 } else // Binning parameters have been given
113 {
114 if (intLog != nullptr) {
115 createBinnedOutput(intLog);
116 } else if (dblLog != nullptr) {
117 createBinnedOutput(dblLog);
118 }
119 // else if ( dynamic_cast<const TimeSeriesProperty<std::string>*>(log) !=
120 // NULL )
121 //{
122 // TODO: Implement (if anyone ever asks for it).
123 //}
124 else {
125 throw std::runtime_error("This algorithm only supports number-series logs");
126 }
127 }
128}
129
134 // This is the version for integer logs when no binning parameters have been
135 // given and has a data point per log value
136 const int minVal = log->minValue();
137 const int maxVal = log->maxValue();
138 const auto xLength = std::size_t(maxVal - minVal + 1);
139
140 if (xLength > 10000) {
141 g_log.warning() << "Did you really want to create a " << xLength << " row table? This will take some time!\n";
142 }
143
144 // Accumulate things in a local vector before transferring to the table
145 std::vector<int> Y(xLength);
146 const auto numSpec = static_cast<int>(m_inputWorkspace->getNumberHistograms());
147 Progress prog(this, 0.0, 1.0, std::size_t(numSpec) + xLength);
149 for (int spec = 0; spec < numSpec; ++spec) {
151 const IEventList &eventList = m_inputWorkspace->getSpectrum(std::size_t(spec));
152 filterEventList(eventList, minVal, maxVal, log, Y);
153 prog.report();
155 }
157
158 // Create a table workspace to hold the sum.
159 ITableWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().createTable();
160 auto logValues = outputWorkspace->addColumn("int", m_logName);
161 auto counts = outputWorkspace->addColumn("int", "Counts");
162 auto errors = outputWorkspace->addColumn("double", "Error");
163 outputWorkspace->setRowCount(xLength); // One row per log value across the full range
164 // Set plot type
165 logValues->setPlotType(1); // X
166 counts->setPlotType(2); // Y
167 errors->setPlotType(5); // E
168
169 // Transfer the results to the table
170 for (std::size_t i = 0; i < xLength; ++i) {
171 logValues->cell<int>(i) = minVal + int(i);
172 counts->cell<int>(i) = Y[i];
173 errors->cell<double>(i) = std::sqrt(Y[i]);
174 }
175
176 // Columns for normalisation: monitors (if available), time & proton charge
177 addMonitorCounts(outputWorkspace, log, minVal, maxVal);
178 // Add a column to hold the time duration (in seconds) for which the log had a
179 // certain value
180 auto timeCol = outputWorkspace->addColumn("double", "time");
181 // Add a column to hold the proton charge for which the log had a certain
182 // value
183 auto protonChgCol = outputWorkspace->addColumn("double", "proton_charge");
184 // Get hold of the proton charge log for later
185 const TimeSeriesProperty<double> *protonChargeLog = nullptr;
186 try {
187 protonChargeLog = m_inputWorkspace->run().getTimeSeriesProperty<double>("proton_charge");
188 // Set back to NULL if the log is empty or bad things will happen later
189 if (protonChargeLog->realSize() == 0)
190 protonChargeLog = nullptr;
191 } catch (std::exception &) {
192 // Log and carry on if not found. Column will be left empty.
193 g_log.warning("proton_charge log not found in workspace.");
194 }
195
196 // Get a list of the other time-series logs in the input workspace
197 auto otherLogs = getNumberSeriesLogs();
198 // Add a column for each of these 'other' logs
199 for (const auto &otherLog : otherLogs) {
200 auto newColumn = outputWorkspace->addColumn("double", otherLog.first);
201 // Set these columns to be containing X values
202 newColumn->setPlotType(1);
203 }
204
205 // Now to get the average value of other time-varying logs
206 // Loop through the values of the 'main' log
207 for (int value = minVal; value <= maxVal; ++value) {
208 const auto row = std::size_t(value - minVal);
209 // Create a filter giving the times when this log has the current value
210
211 TimeROI timeRoi;
212 const TimeROI *temp = &m_inputWorkspace->run().getTimeROI();
213 // This section ensures that the filter goes to the end of the run
214 if (value == log->lastValue() && protonChargeLog) {
215 const TimeInterval timeAfterLastLogValue(log->lastTime(), m_inputWorkspace->getLastPulseTime());
216 timeRoi = log->makeFilterByValue(value, value, true, timeAfterLastLogValue, 0.0, false, temp);
217 } else {
218 timeRoi = log->makeFilterByValue(value, value, false, TimeInterval(0, 1), 0., false, temp);
219 }
220
221 // Calculate the time covered by this log value and add it to the table
222 Run run(m_inputWorkspace->run());
223 run.setTimeROI(timeRoi);
225 timeCol->cell<double>(row) = run.getTimeROI().durationInSeconds();
226
228 // Sum up the proton charge for this log value
229 if (protonChargeLog) {
232 const double currentConversion = 1.e-6 / 3600.;
233 protonChgCol->cell<double>(row) = run.getProtonCharge() / currentConversion;
234 }
236
237 // filter the logs
238 for (const auto &otherLog : otherLogs) {
239 // Calculate the average value of each 'other' log for the current value
240 // of the main log
241 // Have to (maybe inefficiently) fetch back column by name - move outside
242 // loop if too slow
243 outputWorkspace->getColumn(otherLog.first)->cell<double>(row) = run.getTimeAveragedValue(otherLog.first);
244 }
245
246 prog.report();
247 }
248
249 setProperty("OutputWorkspace", outputWorkspace);
250}
251
263void SumEventsByLogValue::filterEventList(const API::IEventList &eventList, const int minVal, const int maxVal,
264 const Kernel::TimeSeriesProperty<int> *log, std::vector<int> &Y) {
265 if (log->realSize() == 0)
266 return;
267
268 const auto pulseTimes = eventList.getPulseTimes();
269 for (const auto &pulseTime : pulseTimes) {
270 // Find the value of the log at the time of this event
271 // This algorithm is really concerned with 'slow' logs so we don't care
272 // about
273 // the time of the event within the pulse.
274 // NB: If the pulse time is before the first log entry, we get the first
275 // value.
276 const int logValue = log->getSingleValue(pulseTime);
277
278 if (logValue >= minVal && logValue <= maxVal) {
279 // In this scenario it's easy to know what bin to increment
281 ++Y[logValue - minVal];
282 }
283 }
284}
285
295 const TimeSeriesProperty<int> *log, const int minVal, const int maxVal) {
296 DataObjects::EventWorkspace_const_sptr monitorWorkspace = getProperty("MonitorWorkspace");
297 // If no monitor workspace was given, there's nothing to do
298 if (!monitorWorkspace)
299 return;
300
301 const auto &spectrumInfo = monitorWorkspace->spectrumInfo();
302
303 const auto xLength = std::size_t(maxVal - minVal + 1);
304 // Loop over the spectra - there will be one per monitor
305 for (std::size_t spec = 0; spec < monitorWorkspace->getNumberHistograms(); ++spec) {
306 try {
307 // Create a column for this monitor
308 const std::string monitorName = spectrumInfo.detector(spec).getName();
309 auto monitorCounts = outputWorkspace->addColumn("int", monitorName);
310 const IEventList &eventList = monitorWorkspace->getSpectrum(spec);
311 // Accumulate things in a local vector before transferring to the table
312 // workspace
313 std::vector<int> Y(xLength);
314 filterEventList(eventList, minVal, maxVal, log, Y);
315 // Transfer the results to the table
316 for (std::size_t i = 0; i < xLength; ++i) {
317 monitorCounts->cell<int>(i) = Y[i];
318 }
319 } catch (Exception::NotFoundError &) {
320 // ADARA-generated nexus files have sometimes been seen to contain
321 // 'phantom' monitors that aren't in the IDF.
322 // This handles that by ignoring those spectra.
323 }
324 }
325}
326
333std::vector<std::pair<std::string, const Kernel::ITimeSeriesProperty *>> SumEventsByLogValue::getNumberSeriesLogs() {
334 std::vector<std::pair<std::string, const Kernel::ITimeSeriesProperty *>> numberSeriesProps;
335 const auto &logs = m_inputWorkspace->run().getLogData();
336 for (const auto &log : logs) {
337 const std::string logName = log->name();
338 // Don't add the log that's the one being summed against
339 if (logName == m_logName)
340 continue;
341 // Exclude the proton charge log as we have a separate column for the sum of
342 // that
343 if (logName == "proton_charge")
344 continue;
345 // Try to cast to an ITimeSeriesProperty
346 auto tsp = dynamic_cast<const ITimeSeriesProperty *>(log);
347 // Move on to the next one if this is not a TSP
348 if (tsp == nullptr)
349 continue;
350 // Don't keep ones with only one entry
351 // if ( tsp->realSize() < 2 ) continue;
352 // Now make sure it's either an int or double tsp, and if so add log to the
353 // list
354 if (dynamic_cast<TimeSeriesProperty<double> *>(log) || dynamic_cast<TimeSeriesProperty<int> *>(log)) {
355 numberSeriesProps.emplace_back(logName, tsp);
356 }
357 }
358
359 return numberSeriesProps;
360}
361
367 // If only the number of bins was given, add the min & max values of the log
368 if (m_binningParams.size() == 1) {
369 m_binningParams.insert(m_binningParams.begin(), log->minValue());
370 m_binningParams.emplace_back(log->maxValue() * 1.000001); // Make it a tiny bit larger to cover full range
371 }
372
373 // XValues will be resized in createAxisFromRebinParams()
374 std::vector<double> XValues;
375 const int XLength = VectorHelper::createAxisFromRebinParams(m_binningParams, XValues);
376 assert((int)XValues.size() == XLength);
377
378 // Create the output workspace - the factory will give back a Workspace2D
379 MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create("Workspace2D", 1, XLength, XLength - 1);
380 // Copy the bin boundaries into the output workspace
381 outputWorkspace->mutableX(0) = XValues;
382 outputWorkspace->getAxis(0)->title() = m_logName;
383 outputWorkspace->setYUnit("Counts");
384
385 auto &Y = outputWorkspace->mutableY(0);
386 const auto numSpec = static_cast<int>(m_inputWorkspace->getNumberHistograms());
387 Progress prog(this, 0.0, 1.0, numSpec);
389 for (int spec = 0; spec < numSpec; ++spec) {
391 const IEventList &eventList = m_inputWorkspace->getSpectrum(spec);
392 const auto pulseTimes = eventList.getPulseTimes();
393 for (const auto &pulseTime : pulseTimes) {
394 // Find the value of the log at the time of this event
395 const double logValue = log->getSingleValue(pulseTime);
396 if (logValue >= XValues.front() && logValue < XValues.back()) {
398 ++Y[VectorHelper::getBinIndex(XValues, logValue)];
399 }
400 }
401
402 prog.report();
404 }
406
407 // The errors are the sqrt of the counts so long as we don't deal with
408 // weighted events.
409 std::transform(Y.cbegin(), Y.cend(), outputWorkspace->mutableE(0).begin(),
410 static_cast<double (*)(double)>(std::sqrt));
411
412 setProperty("OutputWorkspace", outputWorkspace);
413}
414
415} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
#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 PARALLEL_ATOMIC
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
void interruption_point()
This is called during long-running operations, and check if the algorithm has requested that it be ca...
IEventList : Interface to Mantid::DataObjects::EventList class, used to expose to PythonAPI.
Definition IEventList.h:26
virtual std::vector< Mantid::Types::Core::DateAndTime > getPulseTimes() const =0
Return the list of pulse time values.
virtual void removeDataOutsideTimeROI()
For the time series properties, remove values according to TimeROI.
const Kernel::TimeROI & getTimeROI() const
double getTimeAveragedValue(const std::string &name) const
Get the time averaged value for a log.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
void setTimeROI(const Kernel::TimeROI &timeroi) override
Definition Run.cpp:272
double getProtonCharge() const
Get the proton charge.
Definition Run.cpp:300
A property class for workspaces.
void init() override
Virtual method - must be overridden by concrete algorithm.
std::vector< double > m_binningParams
The optional binning parameters.
void addMonitorCounts(const API::ITableWorkspace_sptr &outputWorkspace, const Kernel::TimeSeriesProperty< int > *log, const int minVal, const int maxVal)
Looks for monitor event data and, if found, adds columns to the output table corresponding to the mon...
void filterEventList(const API::IEventList &eventList, const int minVal, const int maxVal, const Kernel::TimeSeriesProperty< int > *log, std::vector< int > &Y)
Goes through an event list assigning events to the output vector according to the log value at the ti...
void exec() override
Virtual method - must be overridden by concrete algorithm.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
void createBinnedOutput(const Kernel::TimeSeriesProperty< T > *log)
Create a single-spectrum Workspace2D containing the integrated counts versus log value,...
void createTableOutput(const Kernel::TimeSeriesProperty< int > *log)
Produces the table workspace output for an integer TimeSeriesProperty.
std::string m_logName
The name of the log to sum against.
DataObjects::EventWorkspace_const_sptr m_inputWorkspace
The input workspace.
std::vector< std::pair< std::string, const Kernel::ITimeSeriesProperty * > > getNumberSeriesLogs()
Searches the input workspace for int or double TimeSeriesProperty's other than the one being summed a...
Support for a property that holds an array of values.
Exception for when an item is not found in a collection.
Definition Exception.h:145
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
A non-templated interface to a TimeSeriesProperty.
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
Validator to check that a property is not left empty.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Base class for properties.
Definition Property.h:94
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Represents a time interval.
Definition DateAndTime.h:25
TimeROI : Object that holds information about when the time measurement was active.
Definition TimeROI.h:18
double durationInSeconds() const
Duration of the whole TimeROI.
Definition TimeROI.cpp:624
A specialised Property class for holding a series of time-value pairs.
TYPE minValue() const
Returns the minimum value found in the series.
TYPE maxValue() const
Returns the maximum value found in the series.
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.
Types::Core::DateAndTime lastTime() const
Returns the last time.
TYPE lastValue() const
Returns the last value.
TYPE getSingleValue(const Types::Core::DateAndTime &t) const
Returns the value at a particular time.
int realSize() const override
Returns the real size of the time series property map:
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const EventWorkspace > EventWorkspace_const_sptr
shared pointer to a const Workspace2D
std::shared_ptr< EventWorkspace > EventWorkspace_sptr
shared pointer to the EventWorkspace class
MANTID_KERNEL_DLL int getBinIndex(const std::vector< double > &bins, const double value)
Return the index into a vector of bin boundaries for a particular X value.
int MANTID_KERNEL_DLL createAxisFromRebinParams(const std::vector< double > &params, std::vector< double > &xnew, const bool resize_xnew=true, const bool full_bins_only=false, const double xMinHint=std::nan(""), const double xMaxHint=std::nan(""), const bool useReverseLogarithmic=false, const double power=-1)
Creates a new output X array given a 'standard' set of rebinning parameters.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54