Mantid
Loading...
Searching...
No Matches
GetDetectorOffsets.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"
18#include "MantidKernel/Unit.h"
19
20namespace Mantid::Algorithms {
21
22// Register the class into the algorithm factory
23DECLARE_ALGORITHM(GetDetectorOffsets)
24
25using namespace Kernel;
27using namespace Algorithms::PeakParameterHelper;
28using namespace API;
29using std::size_t;
30using namespace DataObjects;
31
32//-----------------------------------------------------------------------------------------
36
37 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
38
39 "A 2D workspace with X values of d-spacing");
40
41 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
42 mustBePositive->setLower(0);
43
44 declareProperty("Step", 0.001, mustBePositive, "Step size used to bin d-spacing data");
45 declareProperty("DReference", 2.0, mustBePositive, "Center of reference peak in d-space");
46 declareProperty("XMin", 0.0, "Minimum of CrossCorrelation data to search for peak, usually negative");
47 declareProperty("XMax", 0.0, "Maximum of CrossCorrelation data to search for peak, usually positive");
48
49 declareProperty(std::make_unique<FileProperty>("GroupingFileName", "", FileProperty::OptionalSave, ".cal"),
50 "Optional: The name of the output CalFile to save the "
51 "generated OffsetsWorkspace.");
52 declareProperty(std::make_unique<WorkspaceProperty<OffsetsWorkspace>>("OutputWorkspace", "", Direction::Output),
53 "An output workspace containing the offsets.");
54
55 // Mantid's python API _requires_ a non empty-string name for any Output workspace, even when 'PropertyMode::Optional'
56 // is specified.
57 declareProperty(std::make_unique<WorkspaceProperty<MaskWorkspace>>("MaskWorkspace", "_empty_", Direction::Output,
59 "Mask workspace (optional input / output workspace):"
60 " when specified, if the workspace already exists, any incoming masked detectors will be combined"
61 " with any additional outgoing masked detectors detected by the algorithm");
62
63 // Only keep peaks
64 declareProperty("PeakFunction", "Gaussian",
65 std::make_shared<StringListValidator>(FunctionFactory::Instance().getFunctionNames<IPeakFunction>()),
66 "The function type for fitting the peaks.");
67 declareProperty(std::make_unique<Kernel::PropertyWithValue<bool>>("EstimateFWHM", false),
68 "Whether to esimate FWHM of peak function when estimating fit parameters");
69 declareProperty("MaxOffset", 1.0, "Maximum absolute value of offsets; default is 1");
70
71 /* Signed mode calculates offset in number of bins */
72 std::vector<std::string> modes{"Relative", "Absolute", "Signed"};
73
74 declareProperty("OffsetMode", "Relative", std::make_shared<StringListValidator>(modes),
75 "Whether to calculate a relative, absolute, or signed offset");
76 declareProperty("DIdeal", 2.0, mustBePositive,
77 "The known peak centre value from the NIST standard "
78 "information, this is only used in Absolute OffsetMode.");
79}
80
81std::map<std::string, std::string> GetDetectorOffsets::validateInputs() {
82 std::map<std::string, std::string> result;
83
84 MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
85 if (!inputWS) {
86 result["InputWorkspace"] = "The InputWorkspace must be a MatrixWorkspace.";
87 return result;
88 }
89
90 const auto unit = inputWS->getAxis(0)->unit()->caption();
91 if (unit != "Bins of Shift" && unit != "d-Spacing") {
92 const auto unitErrorMsg = "GetDetectorOffsets only supports input workspaces with units 'Bins of Shift' or "
93 "'d-Spacing', your unit was : " +
94 unit;
95 result["InputWorkspace"] = unitErrorMsg;
96 return result;
97 }
98
99 if (MaskWorkspace_const_sptr maskWS = getProperty("MaskWorkspace")) {
100 // detectors which are monitors are not included in the mask
101 const bool maskContainsAllDetIDs = maskWS->containsDetIDs(inputWS->getInstrument()->getDetectorIDs(true));
102
103 if (!maskContainsAllDetIDs) {
104 result["MaskWorkspace"] =
105 "incoming mask workspace must be fully specified, containing an entry for each detid in the input workspace";
106 } else if (maskWS->getInstrument()->getNumberDetectors(true) !=
107 inputWS->getInstrument()->getNumberDetectors(true)) {
108 result["MaskWorkspace"] = "incoming mask workspace must have the same instrument as the input workspace";
109 } else if (maskWS->getNumberHistograms() != inputWS->getInstrument()->getNumberDetectors(true)) {
110 result["MaskWorkspace"] = "incoming mask workspace must have one spectrum per detector";
111 }
112 }
113 return result;
114}
115//-----------------------------------------------------------------------------------------
122 inputW = getProperty("InputWorkspace");
123 m_Xmin = getProperty("XMin");
124 m_Xmax = getProperty("XMax");
125 m_maxOffset = getProperty("MaxOffset");
126 if (m_Xmin >= m_Xmax)
127 throw std::runtime_error("Must specify m_Xmin<m_Xmax");
128 m_dreference = getProperty("DReference");
129 m_step = getProperty("Step");
130 m_estimateFWHM = getProperty("EstimateFWHM");
131
132 std::string mode_str = getProperty("OffsetMode");
133
134 if (mode_str == "Absolute") {
136 }
137
138 else if (mode_str == "Relative") {
140 }
141
142 else if (mode_str == "Signed") {
144 }
145
146 m_dideal = getProperty("DIdeal");
147
148 int64_t nspec = static_cast<int64_t>(inputW->getNumberHistograms());
149
150 // Create the output OffsetsWorkspace and initialize it to zero.
151 auto outputW = std::make_shared<OffsetsWorkspace>(inputW->getInstrument());
152 size_t N_d = outputW->getNumberHistograms();
153 for (size_t di = 0; di < N_d; ++di)
154 outputW->mutableY(di) = 0.0; // calls detail::FixedLengthVector::assign
155
156 // Use the incoming mask workspace, or start a new one if the workspace does not exist.
157 MaskWorkspace_sptr maskWS;
158 if (!isDefault("MaskWorkspace")) {
159 maskWS = getProperty("MaskWorkspace");
160 }
161 if (!maskWS) {
162 g_log.debug() << "[GetDetectorOffsets]: CREATING new MaskWorkspace.\n";
163 // A new mask is completely cleared at creation.
164 maskWS = std::make_shared<MaskWorkspace>(inputW->getInstrument());
165 } else {
166 g_log.debug() << "[GetDetectorOffsets]: Using EXISTING MaskWorkspace.\n";
167 }
168 // Include any incoming masked detector flags in the mask-workspace values.
169 maskWS->combineFromDetectorMasks(inputW->detectorInfo());
170
171 // Fit all the spectra with a gaussian
172 Progress prog(this, 0.0, 1.0, nspec);
174 for (int64_t wi = 0; wi < nspec; ++wi) {
176 // Get the list of detectors in this pixel
177 const auto &dets = inputW->getSpectrum(static_cast<size_t>(wi)).getDetectorIDs();
178
179 // If the entire spectrum is already masked, there's nothing to do.
180 if (maskWS->isMasked(dets))
181 continue;
182
183 // Fit the peak
184 double offset = fitSpectra(static_cast<size_t>(wi));
185 bool spectrumIsMasked = false;
186 if (std::abs(offset) > m_maxOffset) {
187 g_log.debug() << "[GetDetectorOffsets]: fit failure: offset: " << std::abs(offset)
188 << " is greater than maximum allowed: " << m_maxOffset << ".\n";
189 spectrumIsMasked = true;
190 }
191
192 // Most of the exec time is in FitSpectra, so this critical block should not
193 // be a problem.
194 PARALLEL_CRITICAL(GetDetectorOffsets_setValue) {
195 // Use the same offset for all detectors from this pixel
196 for (const auto &det : dets) {
197 if (spectrumIsMasked) {
198 maskWS->setMasked(det, true);
199 continue;
200 }
201 // Warning: individual detectors in a spectrum may be masked.
202 if (!maskWS->isMasked(det))
203 outputW->setValue(det, offset);
204 }
205 }
206 prog.report();
208 }
210 // Make sure that the output workspaces' detector masks are consistent with the mask values.
211 maskWS->combineToDetectorMasks();
212 maskWS->combineToDetectorMasks(outputW->mutableDetectorInfo());
213
214 if (g_log.getLevel() >= Logger::Priority::PRIO_TRACE) {
215 auto &trace(g_log.getLogStream(Logger::Priority::PRIO_TRACE));
216 trace << "[GetDetectorOffsets]: Computed offsets:\n" << std::endl;
217 for (size_t ns = 0; ns < inputW->getNumberHistograms(); ++ns) {
218 const auto dets = inputW->getSpectrum(ns).getDetectorIDs();
219 for (const auto &det : dets)
220 trace << " " << outputW->getValue(det) << (maskWS->isMasked(det) ? "*" : "") << "\n";
221 }
222 }
223 // Return the output
224 setProperty("OutputWorkspace", outputW);
225
226 // Only return the mask workspace if it was specified.
227 if (!isDefault("MaskWorkspace"))
228 setProperty("MaskWorkspace", maskWS);
229
230 // Also save to .cal file, if requested
231 std::string filename = getProperty("GroupingFileName");
232 if (!filename.empty()) {
233 progress(0.9, "Saving .cal file");
234 auto childAlg = createChildAlgorithm("SaveCalFile");
235 childAlg->setProperty("OffsetsWorkspace", outputW);
236 childAlg->setProperty("MaskWorkspace", maskWS);
237 childAlg->setPropertyValue("Filename", filename);
238 childAlg->executeAsChildAlg();
239 }
240}
241
242//-----------------------------------------------------------------------------------------
248double GetDetectorOffsets::fitSpectra(const size_t wksp_index) {
249 constexpr double FIT_FAILURE = DBL_MAX;
250
251 // Find point of peak centre
252 const auto &yValues = inputW->y(wksp_index);
253 auto it = std::max_element(yValues.cbegin(), yValues.cend());
254
255 // Set the default peak height and location
256 double peakHeight = *it;
257 const double peakLoc = inputW->x(wksp_index)[static_cast<size_t>(it - yValues.cbegin())];
258
259 // Return if peak of Cross Correlation is nan (Happens when spectra is zero)
260 // Pixel with large offset will be masked
261 if (std::isnan(peakHeight))
262 return (FIT_FAILURE);
263
264 IFunction_sptr fun_ptr = createFunction(peakHeight, peakLoc);
265
266 // Try to observe the peak height and location
267 const auto &histogram = inputW->histogram(wksp_index);
268 const auto &vector_x = histogram.points();
269 const auto start_index = findXIndex(vector_x, m_Xmin);
270 const auto stop_index = findXIndex(vector_x, m_Xmax, start_index);
271 // observe parameters if we found a peak range, otherwise use defaults
272 if (start_index != stop_index) {
273 // create a background function
274 auto bkgdFunction = std::dynamic_pointer_cast<IBackgroundFunction>(fun_ptr->getFunction(0));
275 auto peakFunction = std::dynamic_pointer_cast<IPeakFunction>(fun_ptr->getFunction(1));
276 int result = estimatePeakParameters(histogram, std::pair<size_t, size_t>(start_index, stop_index), peakFunction,
277 bkgdFunction, m_estimateFWHM, EstimatePeakWidth::Observation, EMPTY_DBL(), 0.0);
278 if (result != PeakFitResult::GOOD) {
279 g_log.debug() << "ws index: " << wksp_index
280 << " bad result for estimating peak parameters, using default peak height and loc\n";
281 }
282 } else {
283 g_log.notice() << "ws index: " << wksp_index
284 << " range size is zero when estimating peak parameters, using default peak height and loc\n";
285 }
286
287 IAlgorithm_sptr fit_alg;
288 try {
289 // set the ChildAlgorithm no to log as this will be run once per spectra
290 fit_alg = createChildAlgorithm("Fit", -1, -1, false);
291 } catch (Exception::NotFoundError &) {
292 g_log.error("Can't locate Fit algorithm");
293 throw;
294 }
295
296 fit_alg->setProperty("Function", fun_ptr);
297
298 fit_alg->setProperty("InputWorkspace", inputW);
299 fit_alg->setProperty<int>("WorkspaceIndex",
300 static_cast<int>(wksp_index)); // TODO what is the right thing to do here?
301 fit_alg->setProperty("StartX", m_Xmin);
302 fit_alg->setProperty("EndX", m_Xmax);
303 fit_alg->setProperty("MaxIterations", 100);
304
305 fit_alg->executeAsChildAlg();
306 std::string fitStatus = fit_alg->getProperty("OutputStatus");
307 // Pixel with large offset will be masked
308 if (fitStatus != "success") {
309 g_log.debug() << "[GetDetectorOffsets]: Fit algorithm failure: " << fitStatus << "\n";
310 return (FIT_FAILURE);
311 }
312
313 // std::vector<double> params = fit_alg->getProperty("Parameters");
314 API::IFunction_sptr function = fit_alg->getProperty("Function");
315
316 double offset = function->getParameter(3); // params[3]; // f1.PeakCentre
317
319 // factor := factor * (1+offset) for d-spacemap conversion so factor cannot be negative
320 offset *= -1;
321 }
322
323 /* offset relative to the reference */
325 // factor := factor * (1+offset) for d-spacemap conversion so factor cannot be negative
326 offset = -1. * offset * m_step / (m_dreference + offset * m_step);
327 }
328
329 /* Offset relative to the ideal */
331
332 offset = -1. * offset * m_step / (m_dreference + offset * m_step);
333
334 // translated from(DIdeal - FittedPeakCentre)/(FittedPeakCentre)
335 // given by Matt Tucker in ticket #10642
336
337 offset += (m_dideal - m_dreference) / m_dreference;
338 }
339
340 return offset;
341}
342
348IFunction_sptr GetDetectorOffsets::createFunction(const double peakHeight, const double peakLoc) {
349 const FunctionFactoryImpl &creator = FunctionFactory::Instance();
350 auto background = creator.createFunction("LinearBackground");
351 auto peak = std::dynamic_pointer_cast<IPeakFunction>(creator.createFunction(getProperty("PeakFunction")));
352 peak->setHeight(peakHeight);
353 peak->setCentre(peakLoc);
354 const double sigma(10.0);
355 peak->setFwhm(2.0 * std::sqrt(2.0 * M_LN2) * sigma);
356
357 auto fitFunc = new CompositeFunction(); // Takes ownership of the functions
358 fitFunc->addFunction(background);
359 fitFunc->addFunction(peak);
360
361 return std::shared_ptr<IFunction>(fitFunc);
362}
363
364} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
double background
#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_CRITICAL(name)
#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_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.
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.
Kernel::Logger & g_log
Definition Algorithm.h:426
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
bool isDefault(const std::string &name) const
A composite function is a function containing other functions.
@ OptionalSave
to specify a file to write to but an empty string is
The FunctionFactory class is in charge of the creation of concrete instances of fitting functions.
std::shared_ptr< IFunction > createFunction(const std::string &type) const
Creates an instance of a function.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
API::MatrixWorkspace_sptr inputW
A pointer to the input workspace.
double m_Xmax
The end of the X range for fitting.
API::IFunction_sptr createFunction(const double peakHeight, const double peakLoc)
Create a function string from the given parameters and the algorithm inputs.
double m_dreference
The expected peak position in d-spacing (?)
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
double fitSpectra(const size_t wksp_index)
Call Gaussian as a Child Algorithm to fit the peak in a spectrum.
void init() override
Initialisation method.
bool m_estimateFWHM
Flag to estimate fwhm fit parameter.
void exec() override
Executes the algorithm.
double m_Xmin
The start of the X range for fitting.
double m_dideal
The known peak centre value from the NIST standard.
double m_maxOffset
The maximum absolute value of offsets.
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.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
std::ostream & getLogStream(const Priority &priority)
gets the correct log stream for a priority
Definition Logger.cpp:404
int getLevel() const
Returns the Logger's log level.
Definition Logger.cpp:217
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
The concrete, templated class for properties.
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
Definition IFunction.h:749
MANTID_ALGORITHMS_DLL size_t findXIndex(const vector_like &vecx, const double x, const size_t startindex=0)
Get an index of a value in a sorted vector.
MANTID_ALGORITHMS_DLL int estimatePeakParameters(const HistogramData::Histogram &histogram, const std::pair< size_t, size_t > &peak_window, const API::IPeakFunction_sptr &peakfunction, const API::IBackgroundFunction_sptr &bkgdfunction, bool observe_peak_width, const EstimatePeakWidth peakWidthEstimateApproach, const double peakWidthPercentage, const double minPeakHeight)
Estimate peak parameters by 'observation'.
std::shared_ptr< const MaskWorkspace > MaskWorkspace_const_sptr
shared pointer to a const MaskWorkspace
std::shared_ptr< MaskWorkspace > MaskWorkspace_sptr
shared pointer to the MaskWorkspace class
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.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
@ Input
An input workspace.
Definition Property.h:54
@ Output
An output workspace.
Definition Property.h:55