31std::string
const ACCEPT_CHANGES_IN_FUNCTION{
"AcceptChangesInFunctionTooSmall"};
32std::string
const ACCEPT_CHANGES_IN_PARAMETERS{
"AcceptChangesInParameterTooSmall"};
33std::string
const BACKGROUND_TYPE{
"BackgroundType"};
34std::string
const END_INDEX{
"EndWorkspaceIndex"};
35std::string
const FIT_WINDOW_MULTIPLIER{
"FitWindowMultiplier"};
36std::string
const INPUT_WS{
"InputWorkspace"};
37std::string
const OUTPUT_FIT_WS{
"OutputFitWorkspace"};
38std::string
const OUTPUT_PROFILE_WS{
"OutputProfileWorkspace"};
39std::string
const OUTPUT_STATUS{
"OutputStatus"};
40std::string
const LINE_CENTRE{
"LineCentre"};
41std::string
const RANGE_LOWER{
"RangeLower"};
42std::string
const RANGE_UPPER{
"RangeUpper"};
43std::string
const START_INDEX{
"StartWorkspaceIndex"};
44std::string
const USE_FITTED_CENTRE_ON_FAILURE{
"UseFittedLineCentreOnFailure"};
47std::string
const LINEAR_BACKGROUND{
"Linear"};
48std::string
const FLAT_BACKGROUND{
"Flat"};
49std::string
const FALLBACK_STATUS{
"Fit failed; using initial line centre"};
61double median(
const Mantid::HistogramData::HistogramY &y) {
62 auto finiteValues = std::vector<double>{};
63 finiteValues.reserve(
y.size());
64 std::copy_if(
y.cbegin(),
y.cend(), std::back_inserter(finiteValues),
65 [](
double const value) { return std::isfinite(value); });
66 if (finiteValues.empty()) {
67 throw std::runtime_error(
"FindReflectometryLines could not identify an initial line centre.");
72struct PeakParameters {
75 std::optional<double> fwhm;
79 auto const &
x = profile.
x(0);
80 auto const &
y = profile.
y(0);
81 auto maxIndex = std::optional<size_t>{};
83 if (std::isfinite(y[
index]) && (!maxIndex || y[
index] > y[*maxIndex])) {
88 throw std::runtime_error(
"FindReflectometryLines could not identify an initial line centre.");
93 return {
x[*maxIndex], 0.0, std::nullopt};
97 auto left = std::optional<size_t>{};
99 if (std::isfinite(y[
index - 1]) && y[
index - 1] < halfHeight) {
104 auto right = std::optional<size_t>{};
106 if (std::isfinite(y[
index]) && y[
index] < halfHeight) {
112 auto fwhm = std::optional<double>{};
119 return {
x[*maxIndex],
height, fwhm};
135 return "Finds the fractional workspace index corresponding to a reflected or direct line by fitting a Gaussian "
136 "and background to the integrated detector profile.";
142 const bool acceptChangesInParameters) {
144 if (acceptChangesInFunction) {
147 if (acceptChangesInParameters) {
150 return std::find(acceptedStatuses.cbegin(), acceptedStatuses.cend(), fitStatus) != acceptedStatuses.cend();
156 "A reflectometry workspace containing detector spectra.");
158 auto nonNegative = std::make_shared<Kernel::BoundedValidator<int>>();
159 nonNegative->setLower(0);
161 "Workspace index of the first spectrum to include in the detector profile.");
163 "Workspace index of the last spectrum to include in the detector profile.");
167 auto positive = std::make_shared<Kernel::BoundedValidator<double>>();
168 positive->setLower(0.0);
169 positive->setLowerExclusive(
true);
171 "Number of estimated peak FWHMs included on either side of the initial line centre.");
173 auto const backgrounds = std::vector<std::string>{LINEAR_BACKGROUND, FLAT_BACKGROUND};
174 declareProperty(Prop::BACKGROUND_TYPE, LINEAR_BACKGROUND, std::make_shared<Kernel::StringListValidator>(backgrounds),
175 "Background function fitted with the Gaussian. Choose Linear or Flat.");
177 "If true, accept a fit that stopped because changes in the function value became too small.");
179 "If true, accept a fit that stopped because changes in the parameter values became too small.");
181 "If true, use a finite fitted peak centre when Fit completes with an unsuccessful status. If false, "
182 "use the initial line centre.");
186 "The integrated detector profile used for peak fitting, with X values corresponding to input "
187 "workspace indices.");
190 "The Fit output containing the data, fitted curve, and residuals. Not set when the initial peak "
191 "centre is returned.");
195 "The Fit status when a fitted line centre is returned, otherwise reports that the initial line "
201 std::map<std::string, std::string> issues;
208 int const startIndexProperty =
getProperty(Prop::START_INDEX);
209 auto const startIndex =
static_cast<size_t>(startIndexProperty);
210 if (startIndex >=
workspace->getNumberHistograms()) {
211 issues[Prop::START_INDEX] =
"The index must be smaller than the number of spectra in the input workspace.";
214 int const endIndexProperty =
getProperty(Prop::END_INDEX);
215 auto const endIndex =
static_cast<size_t>(endIndexProperty);
216 if (endIndex >=
workspace->getNumberHistograms()) {
217 issues[Prop::END_INDEX] =
"The index must be smaller than the number of spectra in the input workspace.";
218 }
else if (startIndex > endIndex) {
219 issues[Prop::END_INDEX] =
"The index must not be smaller than StartWorkspaceIndex.";
226 issues[Prop::RANGE_UPPER] =
"RangeUpper must be greater than RangeLower.";
234 integration->setAlwaysStoreInADS(
false);
235 integration->setProperty(
"InputWorkspace", inputWorkspace);
236 int const startIndexProperty =
getProperty(Prop::START_INDEX);
237 integration->setProperty(
"StartWorkspaceIndex", startIndexProperty);
239 int const endIndexProperty =
getProperty(Prop::END_INDEX);
240 integration->setProperty(
"EndWorkspaceIndex", endIndexProperty);
243 integration->setProperty(
"RangeLower",
static_cast<double>(
getProperty(Prop::RANGE_LOWER)));
246 integration->setProperty(
"RangeUpper",
static_cast<double>(
getProperty(Prop::RANGE_UPPER)));
248 integration->execute();
250 setCommonBinEdgesForTranspose(*integratedWorkspace);
253 transpose->setAlwaysStoreInADS(
false);
254 transpose->setProperty(
"InputWorkspace", integratedWorkspace);
255 transpose->execute();
258 auto &
x = profileWorkspace->mutableX(0);
259 auto const firstWorkspaceIndex =
static_cast<double>(startIndexProperty);
261 x[
index] = firstWorkspaceIndex +
static_cast<double>(
index);
263 return profileWorkspace;
269 if (!
isDefault(Prop::OUTPUT_PROFILE_WS)) {
270 setProperty(Prop::OUTPUT_PROFILE_WS, profileWorkspace);
274 auto const backgroundLevel = median(profileWorkspace->y(0));
275 auto const initialPeak = estimatePeak(*profileWorkspace, backgroundLevel);
276 if (!initialPeak.fwhm) {
277 g_log.
warning() <<
"Could not estimate the specular peak width. Using the initial line centre.\n";
278 setProperty(Prop::LINE_CENTRE, initialPeak.centre);
283 auto function = API::FunctionFactory::Instance().createFunction(
"CompositeFunction");
285 Kernel::DynamicPointerCastHelper::dynamicPointerCastWithCheck<API::CompositeFunction, API::IFunction>(function);
286 function = API::FunctionFactory::Instance().createFunction(
"Gaussian");
288 Kernel::DynamicPointerCastHelper::dynamicPointerCastWithCheck<API::IPeakFunction, API::IFunction>(function);
289 gaussian->setCentre(initialPeak.centre);
290 gaussian->setFwhm(*initialPeak.fwhm);
291 gaussian->setHeight(initialPeak.height);
292 composite->addFunction(gaussian);
294 auto backgroundFunction = API::FunctionFactory::Instance().createFunction(
295 backgroundType == FLAT_BACKGROUND ?
"FlatBackground" :
"LinearBackground");
296 backgroundFunction->setParameter(
"A0", backgroundLevel);
297 if (backgroundType == LINEAR_BACKGROUND) {
298 backgroundFunction->setParameter(
"A1", 0.0);
300 composite->addFunction(std::move(backgroundFunction));
303 fit->setProperty(
"Function", std::dynamic_pointer_cast<API::IFunction>(composite));
304 fit->setProperty(
"InputWorkspace", profileWorkspace);
305 fit->setProperty(
"WorkspaceIndex", 0);
306 double const fitWindowMultiplier =
getProperty(Prop::FIT_WINDOW_MULTIPLIER);
307 fit->setProperty(
"StartX", initialPeak.centre - fitWindowMultiplier * *initialPeak.fwhm);
308 fit->setProperty(
"EndX", initialPeak.centre + fitWindowMultiplier * *initialPeak.fwhm);
309 fit->setProperty(
"IgnoreInvalidData",
true);
311 fit->setProperty(
"Output",
"__unused_find_reflectometry_lines");
316 }
catch (std::exception
const &
error) {
317 g_log.
warning() <<
"Specular peak fit failed: " <<
error.what() <<
". Using the initial line centre.\n";
318 setProperty(Prop::LINE_CENTRE, initialPeak.centre);
323 std::string
const fitStatus = fit->getProperty(
"OutputStatus");
324 auto const fittedCentre = gaussian->centre();
325 auto const &profileX = profileWorkspace->x(0);
328 bool const useFittedCentreOnFailure =
getProperty(Prop::USE_FITTED_CENTRE_ON_FAILURE);
329 if ((!fitSuccessful && !useFittedCentreOnFailure) || !std::isfinite(fittedCentre) ||
330 fittedCentre < profileX.front() || fittedCentre > profileX.back()) {
331 g_log.
warning() <<
"Specular peak fit was not successful. Using the initial line centre.\n";
332 setProperty(Prop::LINE_CENTRE, initialPeak.centre);
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
double lower
lower and upper bounds on the multiplier, if known
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.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
bool isDefault(const std::string &name) const
Base MatrixWorkspace Abstract Class.
const HistogramData::HistogramX & x(const size_t index) const
const HistogramData::HistogramY & y(const size_t index) const
A property class for workspaces.
FindReflectometryLines3: Finds a fractional workspace index corresponding to a reflected or direct li...
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
const std::vector< std::string > seeAlso() const override
Function to return all of the seeAlso algorithms related to this algorithm.
API::MatrixWorkspace_sptr createProfile(const API::MatrixWorkspace_sptr &inputWorkspace)
int version() const override
function to return a version of the algorithm, must be overridden in all algorithms
static bool fitStatusIsAccepted(const std::string &fitStatus, bool acceptChangesInFunction, bool acceptChangesInParameters)
const std::string category() const override
function to return a category of the algorithm.
const std::string summary() const override
function returns a summary message that will be displayed in the default GUI, and in the help.
void init() override
Virtual method - must be overridden by concrete algorithm.
void exec() override
Virtual method - must be overridden by concrete algorithm.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
virtual TypedValue getProperty(const std::string &name) const =0
Get the value of a property.
void warning(const std::string &msg)
Logs at warning level.
const std::string CHANGES_IN_FUNCTION_TOO_SMALL
Reported by Levenberg-Marquardt when the change in the cost function between iterations has fallen be...
const std::string CHANGES_IN_PARAMETER_TOO_SMALL
Reported by Levenberg-Marquardt when the change in the parameter values between iterations has fallen...
const std::string SUCCESS
Reported when a minimizer has fully converged.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
Statistics getStatistics(const std::vector< TYPE > &data, const unsigned int flags=StatOptions::AllStats)
Return a statistics object for the given data set.
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.
String constants for algorithm's properties.
@ Input
An input workspace.
@ Output
An output workspace.
double median
Median value.