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 +
15#include "MantidAPI/TextAxis.h"
22
23namespace Mantid::Algorithms {
24
25// Register the class into the algorithm factory
26DECLARE_ALGORITHM(GetDetectorOffsets)
27
28using namespace Kernel;
29using namespace Algorithms::PeakParameterHelper;
30using namespace API;
31using std::size_t;
32using namespace DataObjects;
33
34//-----------------------------------------------------------------------------------------
38
39 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
40
41 "A 2D workspace with X values of d-spacing");
42
43 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
44 mustBePositive->setLower(0);
45
46 declareProperty("Step", 0.001, mustBePositive, "Step size used to bin d-spacing data");
47 declareProperty("DReference", 2.0, mustBePositive, "Center of reference peak in d-space");
48 declareProperty("XMin", 0.0, "Minimum of CrossCorrelation data to search for peak, usually negative");
49 declareProperty("XMax", 0.0, "Maximum of CrossCorrelation data to search for peak, usually positive");
50
51 declareProperty(std::make_unique<FileProperty>("GroupingFileName", "", FileProperty::OptionalSave, ".cal"),
52 "Optional: The name of the output CalFile to save the "
53 "generated OffsetsWorkspace.");
54 declareProperty(std::make_unique<WorkspaceProperty<OffsetsWorkspace>>("OutputWorkspace", "", Direction::Output),
55 "An output workspace containing the offsets.");
56 declareProperty(std::make_unique<WorkspaceProperty<>>("MaskWorkspace", "Mask", Direction::Output),
57 "An output workspace containing the mask.");
58 // Only keep peaks
59 declareProperty("PeakFunction", "Gaussian",
60 std::make_shared<StringListValidator>(FunctionFactory::Instance().getFunctionNames<IPeakFunction>()),
61 "The function type for fitting the peaks.");
62 declareProperty(std::make_unique<Kernel::PropertyWithValue<bool>>("EstimateFWHM", false),
63 "Whether to esimate FWHM of peak function when estimating fit parameters");
64 declareProperty("MaxOffset", 1.0, "Maximum absolute value of offsets; default is 1");
65
66 /* Signed mode calculates offset in number of bins */
67 std::vector<std::string> modes{"Relative", "Absolute", "Signed"};
68
69 declareProperty("OffsetMode", "Relative", std::make_shared<StringListValidator>(modes),
70 "Whether to calculate a relative, absolute, or signed offset");
71 declareProperty("DIdeal", 2.0, mustBePositive,
72 "The known peak centre value from the NIST standard "
73 "information, this is only used in Absolute OffsetMode.");
74}
75
76std::map<std::string, std::string> GetDetectorOffsets::validateInputs() {
77 std::map<std::string, std::string> result;
78
79 MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
80 if (!inputWS) {
81 result["InputWorkspace"] = "The InputWorkspace must be a MatrixWorkspace.";
82 return result;
83 }
84 const auto unit = inputWS->getAxis(0)->unit()->caption();
85 const auto unitErrorMsg =
86 "GetDetectorOffsets only supports input workspaces with units 'Bins of Shift' or 'd-Spacing', your unit was : " +
87 unit;
88 if (unit != "Bins of Shift" && unit != "d-Spacing") {
89 result["InputWorkspace"] = unitErrorMsg;
90 }
91 return result;
92}
93//-----------------------------------------------------------------------------------------
100 inputW = getProperty("InputWorkspace");
101 m_Xmin = getProperty("XMin");
102 m_Xmax = getProperty("XMax");
103 m_maxOffset = getProperty("MaxOffset");
104 if (m_Xmin >= m_Xmax)
105 throw std::runtime_error("Must specify m_Xmin<m_Xmax");
106 m_dreference = getProperty("DReference");
107 m_step = getProperty("Step");
108 m_estimateFWHM = getProperty("EstimateFWHM");
109
110 std::string mode_str = getProperty("OffsetMode");
111
112 if (mode_str == "Absolute") {
114 }
115
116 else if (mode_str == "Relative") {
118 }
119
120 else if (mode_str == "Signed") {
122 }
123
124 m_dideal = getProperty("DIdeal");
125
126 int64_t nspec = inputW->getNumberHistograms();
127 // Create the output OffsetsWorkspace
128 auto outputW = std::make_shared<OffsetsWorkspace>(inputW->getInstrument());
129 // Create the output MaskWorkspace
130 auto maskWS = std::make_shared<MaskWorkspace>(inputW->getInstrument());
131 // To get the workspace index from the detector ID
132 const detid2index_map pixel_to_wi = maskWS->getDetectorIDToWorkspaceIndexMap(true);
133
134 // Fit all the spectra with a gaussian
135 Progress prog(this, 0.0, 1.0, nspec);
136 auto &spectrumInfo = maskWS->mutableSpectrumInfo();
138 for (int64_t wi = 0; wi < nspec; ++wi) {
140 // Fit the peak
141 double offset = fitSpectra(wi);
142 double mask = 0.0;
143 if (std::abs(offset) > m_maxOffset) {
144 offset = 0.0;
145 mask = 1.0;
146 }
147
148 // Get the list of detectors in this pixel
149 const auto &dets = inputW->getSpectrum(wi).getDetectorIDs();
150
151 // Most of the exec time is in FitSpectra, so this critical block should not
152 // be a problem.
153 PARALLEL_CRITICAL(GetDetectorOffsets_setValue) {
154 // Use the same offset for all detectors from this pixel
155 for (const auto &det : dets) {
156 outputW->setValue(det, offset);
157 const auto mapEntry = pixel_to_wi.find(det);
158 if (mapEntry == pixel_to_wi.end())
159 continue;
160 const size_t workspaceIndex = mapEntry->second;
161 if (mask == 1.) {
162 // Being masked
163 maskWS->getSpectrum(workspaceIndex).clearData();
164 spectrumInfo.setMasked(workspaceIndex, true);
165 maskWS->mutableY(workspaceIndex)[0] = mask;
166 } else {
167 // Using the detector
168 maskWS->mutableY(workspaceIndex)[0] = mask;
169 }
170 }
171 }
172 prog.report();
174 }
176
177 // Return the output
178 setProperty("OutputWorkspace", outputW);
179 setProperty("MaskWorkspace", maskWS);
180
181 // Also save to .cal file, if requested
182 std::string filename = getProperty("GroupingFileName");
183 if (!filename.empty()) {
184 progress(0.9, "Saving .cal file");
185 auto childAlg = createChildAlgorithm("SaveCalFile");
186 childAlg->setProperty("OffsetsWorkspace", outputW);
187 childAlg->setProperty("MaskWorkspace", maskWS);
188 childAlg->setPropertyValue("Filename", filename);
189 childAlg->executeAsChildAlg();
190 }
191}
192
193//-----------------------------------------------------------------------------------------
199double GetDetectorOffsets::fitSpectra(const int64_t s) {
200 // Find point of peak centre
201 const auto &yValues = inputW->y(s);
202 auto it = std::max_element(yValues.cbegin(), yValues.cend());
203
204 // Set the default peak height and location
205 double peakHeight = *it;
206 const double peakLoc = inputW->x(s)[it - yValues.begin()];
207
208 // Return if peak of Cross Correlation is nan (Happens when spectra is zero)
209 // Pixel with large offset will be masked
210 if (std::isnan(peakHeight))
211 return (1000.);
212
213 IFunction_sptr fun_ptr = createFunction(peakHeight, peakLoc);
214
215 // Try to observe the peak height and location
216 const auto &histogram = inputW->histogram(s);
217 const auto &vector_x = histogram.points();
218 const auto start_index = findXIndex(vector_x, m_Xmin);
219 const auto stop_index = findXIndex(vector_x, m_Xmax, start_index);
220 // observe parameters if we found a peak range, otherwise use defaults
221 if (start_index != stop_index) {
222 // create a background function
223 auto bkgdFunction = std::dynamic_pointer_cast<IBackgroundFunction>(fun_ptr->getFunction(0));
224 auto peakFunction = std::dynamic_pointer_cast<IPeakFunction>(fun_ptr->getFunction(1));
225 int result = estimatePeakParameters(histogram, std::pair<size_t, size_t>(start_index, stop_index), peakFunction,
226 bkgdFunction, m_estimateFWHM, EstimatePeakWidth::Observation, EMPTY_DBL(), 0.0);
227 if (result != PeakFitResult::GOOD) {
228 g_log.debug() << "ws index: " << s
229 << " bad result for observing peak parameters, using default peak height and loc\n";
230 }
231 } else {
232 g_log.notice() << "ws index: " << s
233 << " range size is zero in estimatePeakParameters, using default peak height and loc\n";
234 }
235
236 IAlgorithm_sptr fit_alg;
237 try {
238 // set the ChildAlgorithm no to log as this will be run once per spectra
239 fit_alg = createChildAlgorithm("Fit", -1, -1, false);
240 } catch (Exception::NotFoundError &) {
241 g_log.error("Can't locate Fit algorithm");
242 throw;
243 }
244
245 fit_alg->setProperty("Function", fun_ptr);
246
247 fit_alg->setProperty("InputWorkspace", inputW);
248 fit_alg->setProperty<int>("WorkspaceIndex",
249 static_cast<int>(s)); // TODO what is the right thing to do here?
250 fit_alg->setProperty("StartX", m_Xmin);
251 fit_alg->setProperty("EndX", m_Xmax);
252 fit_alg->setProperty("MaxIterations", 100);
253
254 fit_alg->executeAsChildAlg();
255 std::string fitStatus = fit_alg->getProperty("OutputStatus");
256 // Pixel with large offset will be masked
257 if (fitStatus != "success")
258 return (1000.);
259
260 // std::vector<double> params = fit_alg->getProperty("Parameters");
261 API::IFunction_sptr function = fit_alg->getProperty("Function");
262
263 double offset = function->getParameter(3); // params[3]; // f1.PeakCentre
264
266 // factor := factor * (1+offset) for d-spacemap conversion so factor cannot be
267 // negative
268 offset *= -1;
269 }
270
271 /* offset relative to the reference */
273 // factor := factor * (1+offset) for d-spacemap conversion so factor cannot be
274 // negative
275 offset = -1. * offset * m_step / (m_dreference + offset * m_step);
276 }
277
278 /* Offset relative to the ideal */
280
281 offset = -1. * offset * m_step / (m_dreference + offset * m_step);
282
283 // translated from(DIdeal - FittedPeakCentre)/(FittedPeakCentre)
284 // given by Matt Tucker in ticket #10642
285
286 offset += (m_dideal - m_dreference) / m_dreference;
287 }
288
289 return offset;
290}
291
297IFunction_sptr GetDetectorOffsets::createFunction(const double peakHeight, const double peakLoc) {
299 auto background = creator.createFunction("LinearBackground");
300 auto peak = std::dynamic_pointer_cast<IPeakFunction>(creator.createFunction(getProperty("PeakFunction")));
301 peak->setHeight(peakHeight);
302 peak->setCentre(peakLoc);
303 const double sigma(10.0);
304 peak->setFwhm(2.0 * std::sqrt(2.0 * M_LN2) * sigma);
305
306 auto fitFunc = new CompositeFunction(); // Takes ownership of the functions
307 fitFunc->addFunction(background);
308 fitFunc->addFunction(peak);
309
310 return std::shared_ptr<IFunction>(fitFunc);
311}
312
313} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
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...
Definition: MultiThreaded.h:94
#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.
Definition: Algorithm.cpp:1913
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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.
Definition: Algorithm.cpp:842
Kernel::Logger & g_log
Definition: Algorithm.h:451
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
A composite function is a function containing other functions.
@ OptionalSave
to specify a file to write to but an empty string is
Definition: FileProperty.h:50
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.
void init() override
Initialisation method.
bool m_estimateFWHM
Flag to estimate fwhm fit parameter.
void exec() override
Executes the algorithm.
DataObjects::OffsetsWorkspace_sptr outputW
A pointer to the output workspace.
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.
double fitSpectra(const int64_t s)
Call Gaussian as a Child Algorithm to fit the peak in a spectrum.
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.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void notice(const std::string &msg)
Logs at notice level.
Definition: Logger.cpp:95
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
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:732
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::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.
Definition: MultiThreaded.h:22
std::unordered_map< detid_t, size_t > detid2index_map
Map with key = detector ID, value = workspace index.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54