Mantid
Loading...
Searching...
No Matches
AddLogSmoothed.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
9#include "MantidAPI/Run.h"
16#include "MantidKernel/Spline.h"
18
19using namespace Mantid::Kernel;
20using namespace Mantid::API;
21using Mantid::Types::Core::DateAndTime;
22
23namespace Mantid::Algorithms {
24
25// Register the algorithm into the AlgorithmFactory
26DECLARE_ALGORITHM(AddLogSmoothed)
27
28//----------------------------------------------------------------------------------------------
29
30namespace {
31namespace PropertyNames {
32std::string const INPUT_WKSP("Workspace");
33std::string const LOG_NAME("LogName");
34std::string const SMOOTHING_METHOD("SmoothingMethod");
35std::string const PARAMS("Params");
36std::string const NEW_LOG_NAME("NewLogName");
37} // namespace PropertyNames
38} // namespace
39
40namespace {
41enum class SmoothingMethod { BOXCAR, FFT_ZERO, FFT_BUTTERWORTH, enum_count };
42const std::vector<std::string> smoothingMethods{"BoxCar", "Zeroing", "Butterworth"};
44} // namespace
45
46namespace {
47using namespace Mantid::Types::Core;
48std::vector<double> getUniformXValues(std::vector<double> const &xVec) {
49 std::vector<double> newX;
50 newX.reserve(xVec.size());
51 if (xVec.size() < 2) {
52 newX = xVec;
53 } else {
54 double const xf = xVec.back(), xi = xVec.front();
55 double const dx = (xf - xi) / static_cast<double>(xVec.size() - 1);
56 for (std::size_t i = 0; i < xVec.size(); i++) {
57 newX.push_back(xi + static_cast<double>(i) * dx);
58 }
59 }
60 return newX;
61}
62
63std::vector<DateAndTime> relativeToAbsoluteTime(DateAndTime const &startTime, std::vector<double> const &relTimes) {
64 // Convert time in sec to DateAndTime
65 std::vector<DateAndTime> timeFull;
66 timeFull.reserve(relTimes.size());
67 std::transform(relTimes.begin(), relTimes.end(), std::back_inserter(timeFull),
68 [&startTime](const double time) { return startTime + time; });
69 return timeFull;
70}
71} // namespace
72
73//----------------------------------------------------------------------------------------------
78 "An input/output workspace. The new log will be added to it.");
80 PropertyNames::LOG_NAME, "", std::make_shared<MandatoryValidator<std::string>>(),
81 "The name that will identify the log entry to be smoothed.\nThis log must be a numerical series (double).");
83 PropertyNames::SMOOTHING_METHOD),
84 "The smoothing method to use");
85 declareProperty(std::make_unique<ArrayProperty<int>>(PropertyNames::PARAMS, std::vector<int>()),
86 "The parameters which will be passed to the smoothing function.");
88 PropertyNames::NEW_LOG_NAME, "",
89 "Name of the newly created log. If not specified, the string '_smoothed' will be appended to the original name");
90}
91
92std::map<std::string, std::string> AddLogSmoothed::validateInputs() {
93 std::map<std::string, std::string> issues;
94
95 // validate parameters based on smoothing method chosen
96 SMOOTH type = getPropertyValue(PropertyNames::SMOOTHING_METHOD);
97 std::vector<int> params = getProperty(PropertyNames::PARAMS);
98 switch (type) {
99 case SmoothingMethod::BOXCAR: {
100 if (params.empty()) {
101 issues[PropertyNames::PARAMS] = "Boxcar smoothing requires the window width be passed as parameter";
102 } else if (params[0] < 0) {
103 issues[PropertyNames::PARAMS] = "Boxcar smoothing requires a positive window; given " + std::to_string(params[0]);
104 } else if (params[0] % 2 == 0) {
105 issues[PropertyNames::PARAMS] =
106 "Boxcar smoothing requires an odd window size: " + std::to_string(params[0]) + " is even";
107 }
108 break;
109 }
110 case SmoothingMethod::FFT_ZERO: {
111 if (params.empty()) {
112 issues[PropertyNames::PARAMS] = "FFT zeroing requires the cutoff frequency as a parameter";
113 } else if (params[0] <= 1) {
114 issues[PropertyNames::PARAMS] =
115 "The cutoff in FFT zeroing must be larger than 1; passed " + std::to_string(params[0]);
116 }
117 break;
118 }
119 case SmoothingMethod::FFT_BUTTERWORTH: {
120 if (params.size() < 2) {
121 issues[PropertyNames::PARAMS] =
122 "Butterworth smoothing requires two parameters, passed " + std::to_string(params.size());
123 } else if (params[0] <= 1 || params[1] < 1) {
124 issues[PropertyNames::PARAMS] =
125 "In Butterworth smoothing, cutoff must be greater than 1 and order must be greater than 0";
126 }
127 break;
128 }
129 default: {
130 issues[PropertyNames::SMOOTHING_METHOD] =
131 "Parameter validation for smoothing method " + std::string(type) + " has not been implemented";
132 }
133 }
134 if (issues.count(PropertyNames::PARAMS))
135 issues[PropertyNames::SMOOTHING_METHOD] = issues[PropertyNames::PARAMS];
136
137 // validate input workspace: must have a log with LogName
138 std::string logName = getPropertyValue(PropertyNames::LOG_NAME);
140 if (!ws) {
141 issues[PropertyNames::INPUT_WKSP] = "No matrix workspace specified for input workspace";
142 return issues;
143 }
144 Run const &run = ws->run();
145 if (!run.hasProperty(logName)) {
146 issues[PropertyNames::LOG_NAME] = "Log " + logName + " not found in the workspace sample logs.";
147 return issues;
148 }
149 auto const *tsp = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty(logName));
150 if (!tsp) {
151 issues[PropertyNames::LOG_NAME] = "Log " + logName + " must be a numerical time series (TimeSeries<double>).";
152 } else {
153 std::size_t const minBoxCarSize = (params.empty() ? 0 : static_cast<std::size_t>(params[0]));
154 std::size_t const MIN_SPLINE_POINTS{5UL}; // minimum points needed for spline fits
155 std::size_t minSize = (type == SmoothingMethod::BOXCAR ? minBoxCarSize : MIN_SPLINE_POINTS);
156 if (static_cast<std::size_t>(tsp->size()) < minSize) {
157 issues[PropertyNames::LOG_NAME] = "Log " + logName +
158 " has insufficient number of points: " + std::to_string(tsp->size()) + " < " +
159 std::to_string(minSize);
160 }
161 }
162 return issues;
163}
164
165//----------------------------------------------------------------------------------------------
170 std::vector<int> params = getProperty(PropertyNames::PARAMS);
171 std::string logName = getPropertyValue(PropertyNames::LOG_NAME);
172 std::string newLogName = getPropertyValue(PropertyNames::NEW_LOG_NAME);
173 if (newLogName.empty())
174 newLogName = logName + "_smoothed";
175
176 // retrieve the time series data
177 Run &run = ws->mutableRun();
178 auto *tsp = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty(logName));
179 std::vector<double> values = tsp->valuesAsVector();
180 std::vector<double> times = tsp->timesAsVectorSeconds();
181
182 // Perform smoothing
183 auto output = std::make_unique<TimeSeriesProperty<double>>(newLogName);
184 SMOOTH smoothingMethod = getPropertyValue(PropertyNames::SMOOTHING_METHOD);
185 switch (smoothingMethod) {
186 case SmoothingMethod::BOXCAR: {
187 std::vector<double> newValues = Smoothing::boxcarSmooth(values, params[0]);
188 output->addValues(tsp->timesAsVector(), newValues);
189 break;
190 }
191 case SmoothingMethod::FFT_ZERO: {
192 std::vector<double> flatTimes = getUniformXValues(times);
193 std::vector<double> splinedValues = CubicSpline<double, double>::getSplinedYValues(flatTimes, times, values);
194 std::vector<double> smoothedValues = Smoothing::fftSmooth(splinedValues, params[0]);
195 output->addValues(relativeToAbsoluteTime(tsp->nthTime(0), flatTimes), smoothedValues);
196 break;
197 }
198 case SmoothingMethod::FFT_BUTTERWORTH: {
199 std::vector<double> flatTimes = getUniformXValues(times);
200 std::vector<double> splinedValues = CubicSpline<double, double>::getSplinedYValues(flatTimes, times, values);
201 std::vector<double> smoothedValues = Smoothing::fftButterworthSmooth(splinedValues, params[0], params[1]);
202 output->addValues(relativeToAbsoluteTime(tsp->nthTime(0), flatTimes), smoothedValues);
203 break;
204 }
205 default:
206 throw Mantid::Kernel::Exception::NotImplementedError("Smoothing method " + std::string(smoothingMethod) +
207 " has not been implemented");
208 }
209
210 // Add the log
211 run.addProperty(output.release(), true);
212
213 g_log.notice() << "Added log named " << newLogName << " to " << ws->getName() << '\n';
214}
215
216} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
A property class for workspaces.
std::map< std::string, std::string > validateInputs() override
Validate input parameters.
void init() override
Initialise the properties.
void exec() override
Run the algorithm.
Support for a property that holds an array of values.
static std::vector< Y > getSplinedYValues(std::span< X const > newX, std::span< X const > x, std::span< Y const > y)
Definition Spline.h:84
A concrete property based on user options of a finite list of strings.
Marks code as not implemented yet.
Definition Exception.h:138
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
Validator to check that a property is not left empty.
A specialised Property class for holding a series of time-value pairs.
std::vector< TYPE > valuesAsVector() const
Return the time series's values (unfiltered) as a vector<TYPE>
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::string const INPUT_WKSP("InputWorkspace")
std::vector< T > boxcarSmooth(std::vector< T > const &input, unsigned int const numPoints)
Performs boxcar (moving average) smoothing on the input data.
std::vector< Y > fftButterworthSmooth(std::vector< Y > const &input, unsigned const cutoff, unsigned const order)
Performs FFT smoothing on the input data, using a Butterworth filter NOTE: the input data MUST be def...
std::vector< Y > fftSmooth(std::vector< Y > const &input, unsigned const cutoff)
Performs FFT smoothing on the input data, with high frequencies set to zero NOTE: the input data MUST...
const std::string PARAMS("Params")
const std::string INPUT_WKSP("InputWorkspace")
std::string to_string(const wide_integer< Bits, Signed > &n)
@ InOut
Both an input & output workspace.
Definition Property.h:55