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